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