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