• 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 
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 
SipHelper(SipStack sipStack, SipProvider sipProvider)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 
createFromHeader(SipProfile profile, String tag)90     private FromHeader createFromHeader(SipProfile profile, String tag)
91             throws ParseException {
92         return mHeaderFactory.createFromHeader(profile.getSipAddress(), tag);
93     }
94 
createToHeader(SipProfile profile)95     private ToHeader createToHeader(SipProfile profile) throws ParseException {
96         return createToHeader(profile, null);
97     }
98 
createToHeader(SipProfile profile, String tag)99     private ToHeader createToHeader(SipProfile profile, String tag)
100             throws ParseException {
101         return mHeaderFactory.createToHeader(profile.getSipAddress(), tag);
102     }
103 
createCallIdHeader()104     private CallIdHeader createCallIdHeader() {
105         return mSipProvider.getNewCallId();
106     }
107 
createCSeqHeader(String method)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 
createMaxForwardsHeader()114     private MaxForwardsHeader createMaxForwardsHeader()
115             throws InvalidArgumentException {
116         return mHeaderFactory.createMaxForwardsHeader(70);
117     }
118 
createMaxForwardsHeader(int max)119     private MaxForwardsHeader createMaxForwardsHeader(int max)
120             throws InvalidArgumentException {
121         return mHeaderFactory.createMaxForwardsHeader(max);
122     }
123 
getListeningPoint()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 
createViaHeaders()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 
createContactHeader(SipProfile profile)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 
createWildcardContactHeader()160     private ContactHeader createWildcardContactHeader() {
161         ContactHeader contactHeader  = mHeaderFactory.createContactHeader();
162         contactHeader.setWildCard();
163         return contactHeader;
164     }
165 
createSipUri(String username, String transport, ListeningPoint lp)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 
sendKeepAlive(SipProfile userProfile, String tag)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 
sendRegister(SipProfile userProfile, String tag, int expiry)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 
createRequest(String requestType, SipProfile userProfile, String tag)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(
219                 userProfile.getUriString().replaceFirst(
220                 userProfile.getUserName() + "@", ""));
221         List<ViaHeader> viaHeaders = createViaHeaders();
222         CallIdHeader callIdHeader = createCallIdHeader();
223         CSeqHeader cSeqHeader = createCSeqHeader(requestType);
224         MaxForwardsHeader maxForwards = createMaxForwardsHeader();
225         Request request = mMessageFactory.createRequest(requestURI,
226                 requestType, callIdHeader, cSeqHeader, fromHeader,
227                 toHeader, viaHeaders, maxForwards);
228         Header userAgentHeader = mHeaderFactory.createHeader("User-Agent",
229                 "SIPAUA/0.1.001");
230         request.addHeader(userAgentHeader);
231         return request;
232     }
233 
handleChallenge(ResponseEvent responseEvent, AccountManager accountManager)234     public ClientTransaction handleChallenge(ResponseEvent responseEvent,
235             AccountManager accountManager) throws SipException {
236         AuthenticationHelper authenticationHelper =
237                 ((SipStackExt) mSipStack).getAuthenticationHelper(
238                         accountManager, mHeaderFactory);
239         ClientTransaction tid = responseEvent.getClientTransaction();
240         ClientTransaction ct = authenticationHelper.handleChallenge(
241                 responseEvent.getResponse(), tid, mSipProvider, 5);
242         if (DEBUG) Log.d(TAG, "send request with challenge response: "
243                 + ct.getRequest());
244         ct.sendRequest();
245         return ct;
246     }
247 
sendInvite(SipProfile caller, SipProfile callee, String sessionDescription, String tag)248     public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,
249             String sessionDescription, String tag)
250             throws SipException {
251         try {
252             FromHeader fromHeader = createFromHeader(caller, tag);
253             ToHeader toHeader = createToHeader(callee);
254             SipURI requestURI = callee.getUri();
255             List<ViaHeader> viaHeaders = createViaHeaders();
256             CallIdHeader callIdHeader = createCallIdHeader();
257             CSeqHeader cSeqHeader = createCSeqHeader(Request.INVITE);
258             MaxForwardsHeader maxForwards = createMaxForwardsHeader();
259 
260             Request request = mMessageFactory.createRequest(requestURI,
261                     Request.INVITE, callIdHeader, cSeqHeader, fromHeader,
262                     toHeader, viaHeaders, maxForwards);
263 
264             request.addHeader(createContactHeader(caller));
265             request.setContent(sessionDescription,
266                     mHeaderFactory.createContentTypeHeader(
267                             "application", "sdp"));
268 
269             ClientTransaction clientTransaction =
270                     mSipProvider.getNewClientTransaction(request);
271             if (DEBUG) Log.d(TAG, "send INVITE: " + request);
272             clientTransaction.sendRequest();
273             return clientTransaction;
274         } catch (ParseException e) {
275             throw new SipException("sendInvite()", e);
276         }
277     }
278 
sendReinvite(Dialog dialog, String sessionDescription)279     public ClientTransaction sendReinvite(Dialog dialog,
280             String sessionDescription) throws SipException {
281         try {
282             Request request = dialog.createRequest(Request.INVITE);
283             request.setContent(sessionDescription,
284                     mHeaderFactory.createContentTypeHeader(
285                             "application", "sdp"));
286 
287             ClientTransaction clientTransaction =
288                     mSipProvider.getNewClientTransaction(request);
289             if (DEBUG) Log.d(TAG, "send RE-INVITE: " + request);
290             dialog.sendRequest(clientTransaction);
291             return clientTransaction;
292         } catch (ParseException e) {
293             throw new SipException("sendReinvite()", e);
294         }
295     }
296 
getServerTransaction(RequestEvent event)297     private ServerTransaction getServerTransaction(RequestEvent event)
298             throws SipException {
299         ServerTransaction transaction = event.getServerTransaction();
300         if (transaction == null) {
301             Request request = event.getRequest();
302             return mSipProvider.getNewServerTransaction(request);
303         } else {
304             return transaction;
305         }
306     }
307 
308     /**
309      * @param event the INVITE request event
310      */
sendRinging(RequestEvent event, String tag)311     public ServerTransaction sendRinging(RequestEvent event, String tag)
312             throws SipException {
313         try {
314             Request request = event.getRequest();
315             ServerTransaction transaction = getServerTransaction(event);
316 
317             Response response = mMessageFactory.createResponse(Response.RINGING,
318                     request);
319 
320             ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
321             toHeader.setTag(tag);
322             response.addHeader(toHeader);
323             if (DEBUG) Log.d(TAG, "send RINGING: " + response);
324             transaction.sendResponse(response);
325             return transaction;
326         } catch (ParseException e) {
327             throw new SipException("sendRinging()", e);
328         }
329     }
330 
331     /**
332      * @param event the INVITE request event
333      */
sendInviteOk(RequestEvent event, SipProfile localProfile, String sessionDescription, ServerTransaction inviteTransaction)334     public ServerTransaction sendInviteOk(RequestEvent event,
335             SipProfile localProfile, String sessionDescription,
336             ServerTransaction inviteTransaction)
337             throws SipException {
338         try {
339             Request request = event.getRequest();
340             Response response = mMessageFactory.createResponse(Response.OK,
341                     request);
342             response.addHeader(createContactHeader(localProfile));
343             response.setContent(sessionDescription,
344                     mHeaderFactory.createContentTypeHeader(
345                             "application", "sdp"));
346 
347             if (inviteTransaction == null) {
348                 inviteTransaction = getServerTransaction(event);
349             }
350 
351             if (inviteTransaction.getState() != TransactionState.COMPLETED) {
352                 if (DEBUG) Log.d(TAG, "send OK: " + response);
353                 inviteTransaction.sendResponse(response);
354             }
355 
356             return inviteTransaction;
357         } catch (ParseException e) {
358             throw new SipException("sendInviteOk()", e);
359         }
360     }
361 
sendInviteBusyHere(RequestEvent event, ServerTransaction inviteTransaction)362     public void sendInviteBusyHere(RequestEvent event,
363             ServerTransaction inviteTransaction) throws SipException {
364         try {
365             Request request = event.getRequest();
366             Response response = mMessageFactory.createResponse(
367                     Response.BUSY_HERE, request);
368 
369             if (inviteTransaction == null) {
370                 inviteTransaction = getServerTransaction(event);
371             }
372 
373             if (inviteTransaction.getState() != TransactionState.COMPLETED) {
374                 if (DEBUG) Log.d(TAG, "send BUSY HERE: " + response);
375                 inviteTransaction.sendResponse(response);
376             }
377         } catch (ParseException e) {
378             throw new SipException("sendInviteBusyHere()", e);
379         }
380     }
381 
382     /**
383      * @param event the INVITE ACK request event
384      */
sendInviteAck(ResponseEvent event, Dialog dialog)385     public void sendInviteAck(ResponseEvent event, Dialog dialog)
386             throws SipException {
387         Response response = event.getResponse();
388         long cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME))
389                 .getSeqNumber();
390         Request ack = dialog.createAck(cseq);
391         if (DEBUG) Log.d(TAG, "send ACK: " + ack);
392         dialog.sendAck(ack);
393     }
394 
sendBye(Dialog dialog)395     public void sendBye(Dialog dialog) throws SipException {
396         Request byeRequest = dialog.createRequest(Request.BYE);
397         if (DEBUG) Log.d(TAG, "send BYE: " + byeRequest);
398         dialog.sendRequest(mSipProvider.getNewClientTransaction(byeRequest));
399     }
400 
sendCancel(ClientTransaction inviteTransaction)401     public void sendCancel(ClientTransaction inviteTransaction)
402             throws SipException {
403         Request cancelRequest = inviteTransaction.createCancel();
404         if (DEBUG) Log.d(TAG, "send CANCEL: " + cancelRequest);
405         mSipProvider.getNewClientTransaction(cancelRequest).sendRequest();
406     }
407 
sendResponse(RequestEvent event, int responseCode)408     public void sendResponse(RequestEvent event, int responseCode)
409             throws SipException {
410         try {
411             Response response = mMessageFactory.createResponse(
412                     responseCode, event.getRequest());
413             if (DEBUG) Log.d(TAG, "send response: " + response);
414             getServerTransaction(event).sendResponse(response);
415         } catch (ParseException e) {
416             throw new SipException("sendResponse()", e);
417         }
418     }
419 
sendInviteRequestTerminated(Request inviteRequest, ServerTransaction inviteTransaction)420     public void sendInviteRequestTerminated(Request inviteRequest,
421             ServerTransaction inviteTransaction) throws SipException {
422         try {
423             Response response = mMessageFactory.createResponse(
424                     Response.REQUEST_TERMINATED, inviteRequest);
425             if (DEBUG) Log.d(TAG, "send response: " + response);
426             inviteTransaction.sendResponse(response);
427         } catch (ParseException e) {
428             throw new SipException("sendInviteRequestTerminated()", e);
429         }
430     }
431 
getCallId(EventObject event)432     public static String getCallId(EventObject event) {
433         if (event == null) return null;
434         if (event instanceof RequestEvent) {
435             return getCallId(((RequestEvent) event).getRequest());
436         } else if (event instanceof ResponseEvent) {
437             return getCallId(((ResponseEvent) event).getResponse());
438         } else if (event instanceof DialogTerminatedEvent) {
439             Dialog dialog = ((DialogTerminatedEvent) event).getDialog();
440             return getCallId(((DialogTerminatedEvent) event).getDialog());
441         } else if (event instanceof TransactionTerminatedEvent) {
442             TransactionTerminatedEvent e = (TransactionTerminatedEvent) event;
443             return getCallId(e.isServerTransaction()
444                     ? e.getServerTransaction()
445                     : e.getClientTransaction());
446         } else {
447             Object source = event.getSource();
448             if (source instanceof Transaction) {
449                 return getCallId(((Transaction) source));
450             } else if (source instanceof Dialog) {
451                 return getCallId((Dialog) source);
452             }
453         }
454         return "";
455     }
456 
getCallId(Transaction transaction)457     public static String getCallId(Transaction transaction) {
458         return ((transaction != null) ? getCallId(transaction.getRequest())
459                                       : "");
460     }
461 
getCallId(Message message)462     private static String getCallId(Message message) {
463         CallIdHeader callIdHeader =
464                 (CallIdHeader) message.getHeader(CallIdHeader.NAME);
465         return callIdHeader.getCallId();
466     }
467 
getCallId(Dialog dialog)468     private static String getCallId(Dialog dialog) {
469         return dialog.getCallId().getCallId();
470     }
471 }
472