Home | History | Annotate | Download | only in sip
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.sip;
     18 
     19 import gov.nist.javax.sip.SipStackExt;
     20 import gov.nist.javax.sip.clientauthutils.AccountManager;
     21 import gov.nist.javax.sip.clientauthutils.AuthenticationHelper;
     22 
     23 import android.net.sip.SipProfile;
     24 import android.util.Log;
     25 
     26 import java.text.ParseException;
     27 import java.util.ArrayList;
     28 import java.util.EventObject;
     29 import java.util.List;
     30 import javax.sip.ClientTransaction;
     31 import javax.sip.Dialog;
     32 import javax.sip.DialogTerminatedEvent;
     33 import javax.sip.InvalidArgumentException;
     34 import javax.sip.ListeningPoint;
     35 import javax.sip.PeerUnavailableException;
     36 import javax.sip.RequestEvent;
     37 import javax.sip.ResponseEvent;
     38 import javax.sip.ServerTransaction;
     39 import javax.sip.SipException;
     40 import javax.sip.SipFactory;
     41 import javax.sip.SipProvider;
     42 import javax.sip.SipStack;
     43 import javax.sip.Transaction;
     44 import javax.sip.TransactionAlreadyExistsException;
     45 import javax.sip.TransactionTerminatedEvent;
     46 import javax.sip.TransactionUnavailableException;
     47 import javax.sip.TransactionState;
     48 import javax.sip.address.Address;
     49 import javax.sip.address.AddressFactory;
     50 import javax.sip.address.SipURI;
     51 import javax.sip.header.CSeqHeader;
     52 import javax.sip.header.CallIdHeader;
     53 import javax.sip.header.ContactHeader;
     54 import javax.sip.header.FromHeader;
     55 import javax.sip.header.Header;
     56 import javax.sip.header.HeaderFactory;
     57 import javax.sip.header.MaxForwardsHeader;
     58 import javax.sip.header.ToHeader;
     59 import javax.sip.header.ViaHeader;
     60 import javax.sip.message.Message;
     61 import javax.sip.message.MessageFactory;
     62 import javax.sip.message.Request;
     63 import javax.sip.message.Response;
     64 
     65 /**
     66  * Helper class for holding SIP stack related classes and for various low-level
     67  * SIP tasks like sending messages.
     68  */
     69 class SipHelper {
     70     private static final String TAG = SipHelper.class.getSimpleName();
     71     private static final boolean DEBUG = true;
     72 
     73     private SipStack mSipStack;
     74     private SipProvider mSipProvider;
     75     private AddressFactory mAddressFactory;
     76     private HeaderFactory mHeaderFactory;
     77     private MessageFactory mMessageFactory;
     78 
     79     public SipHelper(SipStack sipStack, SipProvider sipProvider)
     80             throws PeerUnavailableException {
     81         mSipStack = sipStack;
     82         mSipProvider = sipProvider;
     83 
     84         SipFactory sipFactory = SipFactory.getInstance();
     85         mAddressFactory = sipFactory.createAddressFactory();
     86         mHeaderFactory = sipFactory.createHeaderFactory();
     87         mMessageFactory = sipFactory.createMessageFactory();
     88     }
     89 
     90     private FromHeader createFromHeader(SipProfile profile, String tag)
     91             throws ParseException {
     92         return mHeaderFactory.createFromHeader(profile.getSipAddress(), tag);
     93     }
     94 
     95     private ToHeader createToHeader(SipProfile profile) throws ParseException {
     96         return createToHeader(profile, null);
     97     }
     98 
     99     private ToHeader createToHeader(SipProfile profile, String tag)
    100             throws ParseException {
    101         return mHeaderFactory.createToHeader(profile.getSipAddress(), tag);
    102     }
    103 
    104     private CallIdHeader createCallIdHeader() {
    105         return mSipProvider.getNewCallId();
    106     }
    107 
    108     private CSeqHeader createCSeqHeader(String method)
    109             throws ParseException, InvalidArgumentException {
    110         long sequence = (long) (Math.random() * 10000);
    111         return mHeaderFactory.createCSeqHeader(sequence, method);
    112     }
    113 
    114     private MaxForwardsHeader createMaxForwardsHeader()
    115             throws InvalidArgumentException {
    116         return mHeaderFactory.createMaxForwardsHeader(70);
    117     }
    118 
    119     private MaxForwardsHeader createMaxForwardsHeader(int max)
    120             throws InvalidArgumentException {
    121         return mHeaderFactory.createMaxForwardsHeader(max);
    122     }
    123 
    124     private ListeningPoint getListeningPoint() throws SipException {
    125         ListeningPoint lp = mSipProvider.getListeningPoint(ListeningPoint.UDP);
    126         if (lp == null) lp = mSipProvider.getListeningPoint(ListeningPoint.TCP);
    127         if (lp == null) {
    128             ListeningPoint[] lps = mSipProvider.getListeningPoints();
    129             if ((lps != null) && (lps.length > 0)) lp = lps[0];
    130         }
    131         if (lp == null) {
    132             throw new SipException("no listening point is available");
    133         }
    134         return lp;
    135     }
    136 
    137     private List<ViaHeader> createViaHeaders()
    138             throws ParseException, SipException {
    139         List<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(1);
    140         ListeningPoint lp = getListeningPoint();
    141         ViaHeader viaHeader = mHeaderFactory.createViaHeader(lp.getIPAddress(),
    142                 lp.getPort(), lp.getTransport(), null);
    143         viaHeader.setRPort();
    144         viaHeaders.add(viaHeader);
    145         return viaHeaders;
    146     }
    147 
    148     private ContactHeader createContactHeader(SipProfile profile)
    149             throws ParseException, SipException {
    150         ListeningPoint lp = getListeningPoint();
    151         SipURI contactURI =
    152                 createSipUri(profile.getUserName(), profile.getProtocol(), lp);
    153 
    154         Address contactAddress = mAddressFactory.createAddress(contactURI);
    155         contactAddress.setDisplayName(profile.getDisplayName());
    156 
    157         return mHeaderFactory.createContactHeader(contactAddress);
    158     }
    159 
    160     private ContactHeader createWildcardContactHeader() {
    161         ContactHeader contactHeader  = mHeaderFactory.createContactHeader();
    162         contactHeader.setWildCard();
    163         return contactHeader;
    164     }
    165 
    166     private SipURI createSipUri(String username, String transport,
    167             ListeningPoint lp) throws ParseException {
    168         SipURI uri = mAddressFactory.createSipURI(username, lp.getIPAddress());
    169         try {
    170             uri.setPort(lp.getPort());
    171             uri.setTransportParam(transport);
    172         } catch (InvalidArgumentException e) {
    173             throw new RuntimeException(e);
    174         }
    175         return uri;
    176     }
    177 
    178     public ClientTransaction sendKeepAlive(SipProfile userProfile, String tag)
    179             throws SipException {
    180         try {
    181             Request request = createRequest(Request.OPTIONS, userProfile, tag);
    182 
    183             ClientTransaction clientTransaction =
    184                     mSipProvider.getNewClientTransaction(request);
    185             clientTransaction.sendRequest();
    186             return clientTransaction;
    187         } catch (Exception e) {
    188             throw new SipException("sendKeepAlive()", e);
    189         }
    190     }
    191 
    192     public ClientTransaction sendRegister(SipProfile userProfile, String tag,
    193             int expiry) throws SipException {
    194         try {
    195             Request request = createRequest(Request.REGISTER, userProfile, tag);
    196             if (expiry == 0) {
    197                 // remove all previous registrations by wildcard
    198                 // rfc3261#section-10.2.2
    199                 request.addHeader(createWildcardContactHeader());
    200             } else {
    201                 request.addHeader(createContactHeader(userProfile));
    202             }
    203             request.addHeader(mHeaderFactory.createExpiresHeader(expiry));
    204 
    205             ClientTransaction clientTransaction =
    206                     mSipProvider.getNewClientTransaction(request);
    207             clientTransaction.sendRequest();
    208             return clientTransaction;
    209         } catch (ParseException e) {
    210             throw new SipException("sendRegister()", e);
    211         }
    212     }
    213 
    214     private Request createRequest(String requestType, SipProfile userProfile,
    215             String tag) throws ParseException, SipException {
    216         FromHeader fromHeader = createFromHeader(userProfile, tag);
    217         ToHeader toHeader = createToHeader(userProfile);
    218         SipURI requestURI = mAddressFactory.createSipURI("sip:"
    219                 + userProfile.getSipDomain());
    220         List<ViaHeader> viaHeaders = createViaHeaders();
    221         CallIdHeader callIdHeader = createCallIdHeader();
    222         CSeqHeader cSeqHeader = createCSeqHeader(requestType);
    223         MaxForwardsHeader maxForwards = createMaxForwardsHeader();
    224         Request request = mMessageFactory.createRequest(requestURI,
    225                 requestType, callIdHeader, cSeqHeader, fromHeader,
    226                 toHeader, viaHeaders, maxForwards);
    227         Header userAgentHeader = mHeaderFactory.createHeader("User-Agent",
    228                 "SIPAUA/0.1.001");
    229         request.addHeader(userAgentHeader);
    230         return request;
    231     }
    232 
    233     public ClientTransaction handleChallenge(ResponseEvent responseEvent,
    234             AccountManager accountManager) throws SipException {
    235         AuthenticationHelper authenticationHelper =
    236                 ((SipStackExt) mSipStack).getAuthenticationHelper(
    237                         accountManager, mHeaderFactory);
    238         ClientTransaction tid = responseEvent.getClientTransaction();
    239         ClientTransaction ct = authenticationHelper.handleChallenge(
    240                 responseEvent.getResponse(), tid, mSipProvider, 5);
    241         if (DEBUG) Log.d(TAG, "send request with challenge response: "
    242                 + ct.getRequest());
    243         ct.sendRequest();
    244         return ct;
    245     }
    246 
    247     public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,
    248             String sessionDescription, String tag)
    249             throws SipException {
    250         try {
    251             FromHeader fromHeader = createFromHeader(caller, tag);
    252             ToHeader toHeader = createToHeader(callee);
    253             SipURI requestURI = callee.getUri();
    254             List<ViaHeader> viaHeaders = createViaHeaders();
    255             CallIdHeader callIdHeader = createCallIdHeader();
    256             CSeqHeader cSeqHeader = createCSeqHeader(Request.INVITE);
    257             MaxForwardsHeader maxForwards = createMaxForwardsHeader();
    258 
    259             Request request = mMessageFactory.createRequest(requestURI,
    260                     Request.INVITE, callIdHeader, cSeqHeader, fromHeader,
    261                     toHeader, viaHeaders, maxForwards);
    262 
    263             request.addHeader(createContactHeader(caller));
    264             request.setContent(sessionDescription,
    265                     mHeaderFactory.createContentTypeHeader(
    266                             "application", "sdp"));
    267 
    268             ClientTransaction clientTransaction =
    269                     mSipProvider.getNewClientTransaction(request);
    270             if (DEBUG) Log.d(TAG, "send INVITE: " + request);
    271             clientTransaction.sendRequest();
    272             return clientTransaction;
    273         } catch (ParseException e) {
    274             throw new SipException("sendInvite()", e);
    275         }
    276     }
    277 
    278     public ClientTransaction sendReinvite(Dialog dialog,
    279             String sessionDescription) throws SipException {
    280         try {
    281             Request request = dialog.createRequest(Request.INVITE);
    282             request.setContent(sessionDescription,
    283                     mHeaderFactory.createContentTypeHeader(
    284                             "application", "sdp"));
    285 
    286             ClientTransaction clientTransaction =
    287                     mSipProvider.getNewClientTransaction(request);
    288             if (DEBUG) Log.d(TAG, "send RE-INVITE: " + request);
    289             dialog.sendRequest(clientTransaction);
    290             return clientTransaction;
    291         } catch (ParseException e) {
    292             throw new SipException("sendReinvite()", e);
    293         }
    294     }
    295 
    296     private ServerTransaction getServerTransaction(RequestEvent event)
    297             throws SipException {
    298         ServerTransaction transaction = event.getServerTransaction();
    299         if (transaction == null) {
    300             Request request = event.getRequest();
    301             return mSipProvider.getNewServerTransaction(request);
    302         } else {
    303             return transaction;
    304         }
    305     }
    306 
    307     /**
    308      * @param event the INVITE request event
    309      */
    310     public ServerTransaction sendRinging(RequestEvent event, String tag)
    311             throws SipException {
    312         try {
    313             Request request = event.getRequest();
    314             ServerTransaction transaction = getServerTransaction(event);
    315 
    316             Response response = mMessageFactory.createResponse(Response.RINGING,
    317                     request);
    318 
    319             ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
    320             toHeader.setTag(tag);
    321             response.addHeader(toHeader);
    322             if (DEBUG) Log.d(TAG, "send RINGING: " + response);
    323             transaction.sendResponse(response);
    324             return transaction;
    325         } catch (ParseException e) {
    326             throw new SipException("sendRinging()", e);
    327         }
    328     }
    329 
    330     /**
    331      * @param event the INVITE request event
    332      */
    333     public ServerTransaction sendInviteOk(RequestEvent event,
    334             SipProfile localProfile, String sessionDescription,
    335             ServerTransaction inviteTransaction)
    336             throws SipException {
    337         try {
    338             Request request = event.getRequest();
    339             Response response = mMessageFactory.createResponse(Response.OK,
    340                     request);
    341             response.addHeader(createContactHeader(localProfile));
    342             response.setContent(sessionDescription,
    343                     mHeaderFactory.createContentTypeHeader(
    344                             "application", "sdp"));
    345 
    346             if (inviteTransaction == null) {
    347                 inviteTransaction = getServerTransaction(event);
    348             }
    349 
    350             if (inviteTransaction.getState() != TransactionState.COMPLETED) {
    351                 if (DEBUG) Log.d(TAG, "send OK: " + response);
    352                 inviteTransaction.sendResponse(response);
    353             }
    354 
    355             return inviteTransaction;
    356         } catch (ParseException e) {
    357             throw new SipException("sendInviteOk()", e);
    358         }
    359     }
    360 
    361     public void sendInviteBusyHere(RequestEvent event,
    362             ServerTransaction inviteTransaction) throws SipException {
    363         try {
    364             Request request = event.getRequest();
    365             Response response = mMessageFactory.createResponse(
    366                     Response.BUSY_HERE, request);
    367 
    368             if (inviteTransaction == null) {
    369                 inviteTransaction = getServerTransaction(event);
    370             }
    371 
    372             if (inviteTransaction.getState() != TransactionState.COMPLETED) {
    373                 if (DEBUG) Log.d(TAG, "send BUSY HERE: " + response);
    374                 inviteTransaction.sendResponse(response);
    375             }
    376         } catch (ParseException e) {
    377             throw new SipException("sendInviteBusyHere()", e);
    378         }
    379     }
    380 
    381     /**
    382      * @param event the INVITE ACK request event
    383      */
    384     public void sendInviteAck(ResponseEvent event, Dialog dialog)
    385             throws SipException {
    386         Response response = event.getResponse();
    387         long cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME))
    388                 .getSeqNumber();
    389         Request ack = dialog.createAck(cseq);
    390         if (DEBUG) Log.d(TAG, "send ACK: " + ack);
    391         dialog.sendAck(ack);
    392     }
    393 
    394     public void sendBye(Dialog dialog) throws SipException {
    395         Request byeRequest = dialog.createRequest(Request.BYE);
    396         if (DEBUG) Log.d(TAG, "send BYE: " + byeRequest);
    397         dialog.sendRequest(mSipProvider.getNewClientTransaction(byeRequest));
    398     }
    399 
    400     public void sendCancel(ClientTransaction inviteTransaction)
    401             throws SipException {
    402         Request cancelRequest = inviteTransaction.createCancel();
    403         if (DEBUG) Log.d(TAG, "send CANCEL: " + cancelRequest);
    404         mSipProvider.getNewClientTransaction(cancelRequest).sendRequest();
    405     }
    406 
    407     public void sendResponse(RequestEvent event, int responseCode)
    408             throws SipException {
    409         try {
    410             Response response = mMessageFactory.createResponse(
    411                     responseCode, event.getRequest());
    412             if (DEBUG) Log.d(TAG, "send response: " + response);
    413             getServerTransaction(event).sendResponse(response);
    414         } catch (ParseException e) {
    415             throw new SipException("sendResponse()", e);
    416         }
    417     }
    418 
    419     public void sendInviteRequestTerminated(Request inviteRequest,
    420             ServerTransaction inviteTransaction) throws SipException {
    421         try {
    422             Response response = mMessageFactory.createResponse(
    423                     Response.REQUEST_TERMINATED, inviteRequest);
    424             if (DEBUG) Log.d(TAG, "send response: " + response);
    425             inviteTransaction.sendResponse(response);
    426         } catch (ParseException e) {
    427             throw new SipException("sendInviteRequestTerminated()", e);
    428         }
    429     }
    430 
    431     public static String getCallId(EventObject event) {
    432         if (event == null) return null;
    433         if (event instanceof RequestEvent) {
    434             return getCallId(((RequestEvent) event).getRequest());
    435         } else if (event instanceof ResponseEvent) {
    436             return getCallId(((ResponseEvent) event).getResponse());
    437         } else if (event instanceof DialogTerminatedEvent) {
    438             Dialog dialog = ((DialogTerminatedEvent) event).getDialog();
    439             return getCallId(((DialogTerminatedEvent) event).getDialog());
    440         } else if (event instanceof TransactionTerminatedEvent) {
    441             TransactionTerminatedEvent e = (TransactionTerminatedEvent) event;
    442             return getCallId(e.isServerTransaction()
    443                     ? e.getServerTransaction()
    444                     : e.getClientTransaction());
    445         } else {
    446             Object source = event.getSource();
    447             if (source instanceof Transaction) {
    448                 return getCallId(((Transaction) source));
    449             } else if (source instanceof Dialog) {
    450                 return getCallId((Dialog) source);
    451             }
    452         }
    453         return "";
    454     }
    455 
    456     public static String getCallId(Transaction transaction) {
    457         return ((transaction != null) ? getCallId(transaction.getRequest())
    458                                       : "");
    459     }
    460 
    461     private static String getCallId(Message message) {
    462         CallIdHeader callIdHeader =
    463                 (CallIdHeader) message.getHeader(CallIdHeader.NAME);
    464         return callIdHeader.getCallId();
    465     }
    466 
    467     private static String getCallId(Dialog dialog) {
    468         return dialog.getCallId().getCallId();
    469     }
    470 }
    471