• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import gov.nist.javax.sip.header.extensions.ReferencesHeader;
23 import gov.nist.javax.sip.header.extensions.ReferredByHeader;
24 import gov.nist.javax.sip.header.extensions.ReplacesHeader;
25 
26 import android.net.sip.SipProfile;
27 import android.util.Log;
28 
29 import java.text.ParseException;
30 import java.util.ArrayList;
31 import java.util.EventObject;
32 import java.util.List;
33 import java.util.regex.Pattern;
34 
35 import javax.sip.ClientTransaction;
36 import javax.sip.Dialog;
37 import javax.sip.DialogTerminatedEvent;
38 import javax.sip.InvalidArgumentException;
39 import javax.sip.ListeningPoint;
40 import javax.sip.PeerUnavailableException;
41 import javax.sip.RequestEvent;
42 import javax.sip.ResponseEvent;
43 import javax.sip.ServerTransaction;
44 import javax.sip.SipException;
45 import javax.sip.SipFactory;
46 import javax.sip.SipProvider;
47 import javax.sip.SipStack;
48 import javax.sip.Transaction;
49 import javax.sip.TransactionAlreadyExistsException;
50 import javax.sip.TransactionTerminatedEvent;
51 import javax.sip.TransactionUnavailableException;
52 import javax.sip.TransactionState;
53 import javax.sip.address.Address;
54 import javax.sip.address.AddressFactory;
55 import javax.sip.address.SipURI;
56 import javax.sip.header.CSeqHeader;
57 import javax.sip.header.CallIdHeader;
58 import javax.sip.header.ContactHeader;
59 import javax.sip.header.FromHeader;
60 import javax.sip.header.Header;
61 import javax.sip.header.HeaderFactory;
62 import javax.sip.header.MaxForwardsHeader;
63 import javax.sip.header.ToHeader;
64 import javax.sip.header.ViaHeader;
65 import javax.sip.message.Message;
66 import javax.sip.message.MessageFactory;
67 import javax.sip.message.Request;
68 import javax.sip.message.Response;
69 
70 /**
71  * Helper class for holding SIP stack related classes and for various low-level
72  * SIP tasks like sending messages.
73  */
74 class SipHelper {
75     private static final String TAG = SipHelper.class.getSimpleName();
76     private static final boolean DEBUG = false;
77     private static final boolean DEBUG_PING = false;
78 
79     private SipStack mSipStack;
80     private SipProvider mSipProvider;
81     private AddressFactory mAddressFactory;
82     private HeaderFactory mHeaderFactory;
83     private MessageFactory mMessageFactory;
84 
SipHelper(SipStack sipStack, SipProvider sipProvider)85     public SipHelper(SipStack sipStack, SipProvider sipProvider)
86             throws PeerUnavailableException {
87         mSipStack = sipStack;
88         mSipProvider = sipProvider;
89 
90         SipFactory sipFactory = SipFactory.getInstance();
91         mAddressFactory = sipFactory.createAddressFactory();
92         mHeaderFactory = sipFactory.createHeaderFactory();
93         mMessageFactory = sipFactory.createMessageFactory();
94     }
95 
createFromHeader(SipProfile profile, String tag)96     private FromHeader createFromHeader(SipProfile profile, String tag)
97             throws ParseException {
98         return mHeaderFactory.createFromHeader(profile.getSipAddress(), tag);
99     }
100 
createToHeader(SipProfile profile)101     private ToHeader createToHeader(SipProfile profile) throws ParseException {
102         return createToHeader(profile, null);
103     }
104 
createToHeader(SipProfile profile, String tag)105     private ToHeader createToHeader(SipProfile profile, String tag)
106             throws ParseException {
107         return mHeaderFactory.createToHeader(profile.getSipAddress(), tag);
108     }
109 
createCallIdHeader()110     private CallIdHeader createCallIdHeader() {
111         return mSipProvider.getNewCallId();
112     }
113 
createCSeqHeader(String method)114     private CSeqHeader createCSeqHeader(String method)
115             throws ParseException, InvalidArgumentException {
116         long sequence = (long) (Math.random() * 10000);
117         return mHeaderFactory.createCSeqHeader(sequence, method);
118     }
119 
createMaxForwardsHeader()120     private MaxForwardsHeader createMaxForwardsHeader()
121             throws InvalidArgumentException {
122         return mHeaderFactory.createMaxForwardsHeader(70);
123     }
124 
createMaxForwardsHeader(int max)125     private MaxForwardsHeader createMaxForwardsHeader(int max)
126             throws InvalidArgumentException {
127         return mHeaderFactory.createMaxForwardsHeader(max);
128     }
129 
getListeningPoint()130     private ListeningPoint getListeningPoint() throws SipException {
131         ListeningPoint lp = mSipProvider.getListeningPoint(ListeningPoint.UDP);
132         if (lp == null) lp = mSipProvider.getListeningPoint(ListeningPoint.TCP);
133         if (lp == null) {
134             ListeningPoint[] lps = mSipProvider.getListeningPoints();
135             if ((lps != null) && (lps.length > 0)) lp = lps[0];
136         }
137         if (lp == null) {
138             throw new SipException("no listening point is available");
139         }
140         return lp;
141     }
142 
createViaHeaders()143     private List<ViaHeader> createViaHeaders()
144             throws ParseException, SipException {
145         List<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(1);
146         ListeningPoint lp = getListeningPoint();
147         ViaHeader viaHeader = mHeaderFactory.createViaHeader(lp.getIPAddress(),
148                 lp.getPort(), lp.getTransport(), null);
149         viaHeader.setRPort();
150         viaHeaders.add(viaHeader);
151         return viaHeaders;
152     }
153 
createContactHeader(SipProfile profile)154     private ContactHeader createContactHeader(SipProfile profile)
155             throws ParseException, SipException {
156         return createContactHeader(profile, null, 0);
157     }
158 
createContactHeader(SipProfile profile, String ip, int port)159     private ContactHeader createContactHeader(SipProfile profile,
160             String ip, int port) throws ParseException,
161             SipException {
162         SipURI contactURI = (ip == null)
163                 ? createSipUri(profile.getUserName(), profile.getProtocol(),
164                         getListeningPoint())
165                 : createSipUri(profile.getUserName(), profile.getProtocol(),
166                         ip, port);
167 
168         Address contactAddress = mAddressFactory.createAddress(contactURI);
169         contactAddress.setDisplayName(profile.getDisplayName());
170 
171         return mHeaderFactory.createContactHeader(contactAddress);
172     }
173 
createWildcardContactHeader()174     private ContactHeader createWildcardContactHeader() {
175         ContactHeader contactHeader  = mHeaderFactory.createContactHeader();
176         contactHeader.setWildCard();
177         return contactHeader;
178     }
179 
createSipUri(String username, String transport, ListeningPoint lp)180     private SipURI createSipUri(String username, String transport,
181             ListeningPoint lp) throws ParseException {
182         return createSipUri(username, transport, lp.getIPAddress(), lp.getPort());
183     }
184 
createSipUri(String username, String transport, String ip, int port)185     private SipURI createSipUri(String username, String transport,
186             String ip, int port) throws ParseException {
187         SipURI uri = mAddressFactory.createSipURI(username, ip);
188         try {
189             uri.setPort(port);
190             uri.setTransportParam(transport);
191         } catch (InvalidArgumentException e) {
192             throw new RuntimeException(e);
193         }
194         return uri;
195     }
196 
sendOptions(SipProfile caller, SipProfile callee, String tag)197     public ClientTransaction sendOptions(SipProfile caller, SipProfile callee,
198             String tag) throws SipException {
199         try {
200             Request request = (caller == callee)
201                     ? createRequest(Request.OPTIONS, caller, tag)
202                     : createRequest(Request.OPTIONS, caller, callee, tag);
203 
204             ClientTransaction clientTransaction =
205                     mSipProvider.getNewClientTransaction(request);
206             clientTransaction.sendRequest();
207             return clientTransaction;
208         } catch (Exception e) {
209             throw new SipException("sendOptions()", e);
210         }
211     }
212 
sendRegister(SipProfile userProfile, String tag, int expiry)213     public ClientTransaction sendRegister(SipProfile userProfile, String tag,
214             int expiry) throws SipException {
215         try {
216             Request request = createRequest(Request.REGISTER, userProfile, tag);
217             if (expiry == 0) {
218                 // remove all previous registrations by wildcard
219                 // rfc3261#section-10.2.2
220                 request.addHeader(createWildcardContactHeader());
221             } else {
222                 request.addHeader(createContactHeader(userProfile));
223             }
224             request.addHeader(mHeaderFactory.createExpiresHeader(expiry));
225 
226             ClientTransaction clientTransaction =
227                     mSipProvider.getNewClientTransaction(request);
228             clientTransaction.sendRequest();
229             return clientTransaction;
230         } catch (ParseException e) {
231             throw new SipException("sendRegister()", e);
232         }
233     }
234 
createRequest(String requestType, SipProfile userProfile, String tag)235     private Request createRequest(String requestType, SipProfile userProfile,
236             String tag) throws ParseException, SipException {
237         FromHeader fromHeader = createFromHeader(userProfile, tag);
238         ToHeader toHeader = createToHeader(userProfile);
239 
240         String replaceStr = Pattern.quote(userProfile.getUserName() + "@");
241         SipURI requestURI = mAddressFactory.createSipURI(
242                 userProfile.getUriString().replaceFirst(replaceStr, ""));
243 
244         List<ViaHeader> viaHeaders = createViaHeaders();
245         CallIdHeader callIdHeader = createCallIdHeader();
246         CSeqHeader cSeqHeader = createCSeqHeader(requestType);
247         MaxForwardsHeader maxForwards = createMaxForwardsHeader();
248         Request request = mMessageFactory.createRequest(requestURI,
249                 requestType, callIdHeader, cSeqHeader, fromHeader,
250                 toHeader, viaHeaders, maxForwards);
251         Header userAgentHeader = mHeaderFactory.createHeader("User-Agent",
252                 "SIPAUA/0.1.001");
253         request.addHeader(userAgentHeader);
254         return request;
255     }
256 
handleChallenge(ResponseEvent responseEvent, AccountManager accountManager)257     public ClientTransaction handleChallenge(ResponseEvent responseEvent,
258             AccountManager accountManager) throws SipException {
259         AuthenticationHelper authenticationHelper =
260                 ((SipStackExt) mSipStack).getAuthenticationHelper(
261                         accountManager, mHeaderFactory);
262         ClientTransaction tid = responseEvent.getClientTransaction();
263         ClientTransaction ct = authenticationHelper.handleChallenge(
264                 responseEvent.getResponse(), tid, mSipProvider, 5);
265         if (DEBUG) Log.d(TAG, "send request with challenge response: "
266                 + ct.getRequest());
267         ct.sendRequest();
268         return ct;
269     }
270 
createRequest(String requestType, SipProfile caller, SipProfile callee, String tag)271     private Request createRequest(String requestType, SipProfile caller,
272             SipProfile callee, String tag) throws ParseException, SipException {
273         FromHeader fromHeader = createFromHeader(caller, tag);
274         ToHeader toHeader = createToHeader(callee);
275         SipURI requestURI = callee.getUri();
276         List<ViaHeader> viaHeaders = createViaHeaders();
277         CallIdHeader callIdHeader = createCallIdHeader();
278         CSeqHeader cSeqHeader = createCSeqHeader(requestType);
279         MaxForwardsHeader maxForwards = createMaxForwardsHeader();
280 
281         Request request = mMessageFactory.createRequest(requestURI,
282                 requestType, callIdHeader, cSeqHeader, fromHeader,
283                 toHeader, viaHeaders, maxForwards);
284 
285         request.addHeader(createContactHeader(caller));
286         return request;
287     }
288 
sendInvite(SipProfile caller, SipProfile callee, String sessionDescription, String tag, ReferredByHeader referredBy, String replaces)289     public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,
290             String sessionDescription, String tag, ReferredByHeader referredBy,
291             String replaces) throws SipException {
292         try {
293             Request request = createRequest(Request.INVITE, caller, callee, tag);
294             if (referredBy != null) request.addHeader(referredBy);
295             if (replaces != null) {
296                 request.addHeader(mHeaderFactory.createHeader(
297                         ReplacesHeader.NAME, replaces));
298             }
299             request.setContent(sessionDescription,
300                     mHeaderFactory.createContentTypeHeader(
301                             "application", "sdp"));
302             ClientTransaction clientTransaction =
303                     mSipProvider.getNewClientTransaction(request);
304             if (DEBUG) Log.d(TAG, "send INVITE: " + request);
305             clientTransaction.sendRequest();
306             return clientTransaction;
307         } catch (ParseException e) {
308             throw new SipException("sendInvite()", e);
309         }
310     }
311 
sendReinvite(Dialog dialog, String sessionDescription)312     public ClientTransaction sendReinvite(Dialog dialog,
313             String sessionDescription) throws SipException {
314         try {
315             Request request = dialog.createRequest(Request.INVITE);
316             request.setContent(sessionDescription,
317                     mHeaderFactory.createContentTypeHeader(
318                             "application", "sdp"));
319 
320             // Adding rport argument in the request could fix some SIP servers
321             // in resolving the initiator's NAT port mapping for relaying the
322             // response message from the other end.
323 
324             ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
325             if (viaHeader != null) viaHeader.setRPort();
326 
327             ClientTransaction clientTransaction =
328                     mSipProvider.getNewClientTransaction(request);
329             if (DEBUG) Log.d(TAG, "send RE-INVITE: " + request);
330             dialog.sendRequest(clientTransaction);
331             return clientTransaction;
332         } catch (ParseException e) {
333             throw new SipException("sendReinvite()", e);
334         }
335     }
336 
getServerTransaction(RequestEvent event)337     public ServerTransaction getServerTransaction(RequestEvent event)
338             throws SipException {
339         ServerTransaction transaction = event.getServerTransaction();
340         if (transaction == null) {
341             Request request = event.getRequest();
342             return mSipProvider.getNewServerTransaction(request);
343         } else {
344             return transaction;
345         }
346     }
347 
348     /**
349      * @param event the INVITE request event
350      */
sendRinging(RequestEvent event, String tag)351     public ServerTransaction sendRinging(RequestEvent event, String tag)
352             throws SipException {
353         try {
354             Request request = event.getRequest();
355             ServerTransaction transaction = getServerTransaction(event);
356 
357             Response response = mMessageFactory.createResponse(Response.RINGING,
358                     request);
359 
360             ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
361             toHeader.setTag(tag);
362             response.addHeader(toHeader);
363             if (DEBUG) Log.d(TAG, "send RINGING: " + response);
364             transaction.sendResponse(response);
365             return transaction;
366         } catch (ParseException e) {
367             throw new SipException("sendRinging()", e);
368         }
369     }
370 
371     /**
372      * @param event the INVITE request event
373      */
sendInviteOk(RequestEvent event, SipProfile localProfile, String sessionDescription, ServerTransaction inviteTransaction, String externalIp, int externalPort)374     public ServerTransaction sendInviteOk(RequestEvent event,
375             SipProfile localProfile, String sessionDescription,
376             ServerTransaction inviteTransaction, String externalIp,
377             int externalPort) throws SipException {
378         try {
379             Request request = event.getRequest();
380             Response response = mMessageFactory.createResponse(Response.OK,
381                     request);
382             response.addHeader(createContactHeader(localProfile, externalIp,
383                     externalPort));
384             response.setContent(sessionDescription,
385                     mHeaderFactory.createContentTypeHeader(
386                             "application", "sdp"));
387 
388             if (inviteTransaction == null) {
389                 inviteTransaction = getServerTransaction(event);
390             }
391 
392             if (inviteTransaction.getState() != TransactionState.COMPLETED) {
393                 if (DEBUG) Log.d(TAG, "send OK: " + response);
394                 inviteTransaction.sendResponse(response);
395             }
396 
397             return inviteTransaction;
398         } catch (ParseException e) {
399             throw new SipException("sendInviteOk()", e);
400         }
401     }
402 
sendInviteBusyHere(RequestEvent event, ServerTransaction inviteTransaction)403     public void sendInviteBusyHere(RequestEvent event,
404             ServerTransaction inviteTransaction) throws SipException {
405         try {
406             Request request = event.getRequest();
407             Response response = mMessageFactory.createResponse(
408                     Response.BUSY_HERE, request);
409 
410             if (inviteTransaction == null) {
411                 inviteTransaction = getServerTransaction(event);
412             }
413 
414             if (inviteTransaction.getState() != TransactionState.COMPLETED) {
415                 if (DEBUG) Log.d(TAG, "send BUSY HERE: " + response);
416                 inviteTransaction.sendResponse(response);
417             }
418         } catch (ParseException e) {
419             throw new SipException("sendInviteBusyHere()", e);
420         }
421     }
422 
423     /**
424      * @param event the INVITE ACK request event
425      */
sendInviteAck(ResponseEvent event, Dialog dialog)426     public void sendInviteAck(ResponseEvent event, Dialog dialog)
427             throws SipException {
428         Response response = event.getResponse();
429         long cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME))
430                 .getSeqNumber();
431         Request ack = dialog.createAck(cseq);
432         if (DEBUG) Log.d(TAG, "send ACK: " + ack);
433         dialog.sendAck(ack);
434     }
435 
sendBye(Dialog dialog)436     public void sendBye(Dialog dialog) throws SipException {
437         Request byeRequest = dialog.createRequest(Request.BYE);
438         if (DEBUG) Log.d(TAG, "send BYE: " + byeRequest);
439         dialog.sendRequest(mSipProvider.getNewClientTransaction(byeRequest));
440     }
441 
sendCancel(ClientTransaction inviteTransaction)442     public void sendCancel(ClientTransaction inviteTransaction)
443             throws SipException {
444         Request cancelRequest = inviteTransaction.createCancel();
445         if (DEBUG) Log.d(TAG, "send CANCEL: " + cancelRequest);
446         mSipProvider.getNewClientTransaction(cancelRequest).sendRequest();
447     }
448 
sendResponse(RequestEvent event, int responseCode)449     public void sendResponse(RequestEvent event, int responseCode)
450             throws SipException {
451         try {
452             Request request = event.getRequest();
453             Response response = mMessageFactory.createResponse(
454                     responseCode, request);
455             if (DEBUG && (!Request.OPTIONS.equals(request.getMethod())
456                     || DEBUG_PING)) {
457                 Log.d(TAG, "send response: " + response);
458             }
459             getServerTransaction(event).sendResponse(response);
460         } catch (ParseException e) {
461             throw new SipException("sendResponse()", e);
462         }
463     }
464 
sendReferNotify(Dialog dialog, String content)465     public void sendReferNotify(Dialog dialog, String content)
466             throws SipException {
467         try {
468             Request request = dialog.createRequest(Request.NOTIFY);
469             request.addHeader(mHeaderFactory.createSubscriptionStateHeader(
470                     "active;expires=60"));
471             // set content here
472             request.setContent(content,
473                     mHeaderFactory.createContentTypeHeader(
474                             "message", "sipfrag"));
475             request.addHeader(mHeaderFactory.createEventHeader(
476                     ReferencesHeader.REFER));
477             if (DEBUG) Log.d(TAG, "send NOTIFY: " + request);
478             dialog.sendRequest(mSipProvider.getNewClientTransaction(request));
479         } catch (ParseException e) {
480             throw new SipException("sendReferNotify()", e);
481         }
482     }
483 
sendInviteRequestTerminated(Request inviteRequest, ServerTransaction inviteTransaction)484     public void sendInviteRequestTerminated(Request inviteRequest,
485             ServerTransaction inviteTransaction) throws SipException {
486         try {
487             Response response = mMessageFactory.createResponse(
488                     Response.REQUEST_TERMINATED, inviteRequest);
489             if (DEBUG) Log.d(TAG, "send response: " + response);
490             inviteTransaction.sendResponse(response);
491         } catch (ParseException e) {
492             throw new SipException("sendInviteRequestTerminated()", e);
493         }
494     }
495 
getCallId(EventObject event)496     public static String getCallId(EventObject event) {
497         if (event == null) return null;
498         if (event instanceof RequestEvent) {
499             return getCallId(((RequestEvent) event).getRequest());
500         } else if (event instanceof ResponseEvent) {
501             return getCallId(((ResponseEvent) event).getResponse());
502         } else if (event instanceof DialogTerminatedEvent) {
503             Dialog dialog = ((DialogTerminatedEvent) event).getDialog();
504             return getCallId(((DialogTerminatedEvent) event).getDialog());
505         } else if (event instanceof TransactionTerminatedEvent) {
506             TransactionTerminatedEvent e = (TransactionTerminatedEvent) event;
507             return getCallId(e.isServerTransaction()
508                     ? e.getServerTransaction()
509                     : e.getClientTransaction());
510         } else {
511             Object source = event.getSource();
512             if (source instanceof Transaction) {
513                 return getCallId(((Transaction) source));
514             } else if (source instanceof Dialog) {
515                 return getCallId((Dialog) source);
516             }
517         }
518         return "";
519     }
520 
getCallId(Transaction transaction)521     public static String getCallId(Transaction transaction) {
522         return ((transaction != null) ? getCallId(transaction.getRequest())
523                                       : "");
524     }
525 
getCallId(Message message)526     private static String getCallId(Message message) {
527         CallIdHeader callIdHeader =
528                 (CallIdHeader) message.getHeader(CallIdHeader.NAME);
529         return callIdHeader.getCallId();
530     }
531 
getCallId(Dialog dialog)532     private static String getCallId(Dialog dialog) {
533         return dialog.getCallId().getCallId();
534     }
535 }
536