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