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 android.net.sip; 18 19 import android.annotation.NonNull; 20 import android.annotation.SdkConstant; 21 import android.annotation.SystemApi; 22 import android.app.PendingIntent; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.os.ServiceManager; 29 import android.telephony.Rlog; 30 31 import java.text.ParseException; 32 import java.util.ArrayList; 33 import java.util.List; 34 35 /** 36 * Provides APIs for SIP tasks, such as initiating SIP connections, and provides access to related 37 * SIP services. This class is the starting point for any SIP actions. You can acquire an instance 38 * of it with {@link #newInstance newInstance()}.</p> 39 * <p>The APIs in this class allows you to:</p> 40 * <ul> 41 * <li>Create a {@link SipSession} to get ready for making calls or listen for incoming calls. See 42 * {@link #createSipSession createSipSession()} and {@link #getSessionFor getSessionFor()}.</li> 43 * <li>Initiate and receive generic SIP calls or audio-only SIP calls. Generic SIP calls may 44 * be video, audio, or other, and are initiated with {@link #open open()}. Audio-only SIP calls 45 * should be handled with a {@link SipAudioCall}, which you can acquire with {@link 46 * #makeAudioCall makeAudioCall()} and {@link #takeAudioCall takeAudioCall()}.</li> 47 * <li>Register and unregister with a SIP service provider, with 48 * {@link #register register()} and {@link #unregister unregister()}.</li> 49 * <li>Verify session connectivity, with {@link #isOpened isOpened()} and 50 * {@link #isRegistered isRegistered()}.</li> 51 * </ul> 52 * <p class="note"><strong>Note:</strong> Not all Android-powered devices support VOIP calls using 53 * SIP. You should always call {@link android.net.sip.SipManager#isVoipSupported 54 * isVoipSupported()} to verify that the device supports VOIP calling and {@link 55 * android.net.sip.SipManager#isApiSupported isApiSupported()} to verify that the device supports 56 * the SIP APIs. Your application must also request the {@link 57 * android.Manifest.permission#INTERNET} and {@link android.Manifest.permission#USE_SIP} 58 * permissions.</p> 59 * 60 * <div class="special reference"> 61 * <h3>Developer Guides</h3> 62 * <p>For more information about using SIP, read the 63 * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a> 64 * developer guide.</p> 65 * </div> 66 */ 67 public class SipManager { 68 /** 69 * The result code to be sent back with the incoming call 70 * {@link PendingIntent}. 71 * @see #open(SipProfile, PendingIntent, SipRegistrationListener) 72 */ 73 public static final int INCOMING_CALL_RESULT_CODE = 101; 74 75 /** 76 * Key to retrieve the call ID from an incoming call intent. 77 * @see #open(SipProfile, PendingIntent, SipRegistrationListener) 78 */ 79 public static final String EXTRA_CALL_ID = "android:sipCallID"; 80 81 /** 82 * Key to retrieve the offered session description from an incoming call 83 * intent. 84 * @see #open(SipProfile, PendingIntent, SipRegistrationListener) 85 */ 86 public static final String EXTRA_OFFER_SD = "android:sipOfferSD"; 87 88 /** 89 * Intent action sent when the SipManager becomes available. 90 * @hide 91 */ 92 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) 93 @SystemApi 94 public static final String ACTION_SIP_SERVICE_UP = 95 "android.net.sip.action.SIP_SERVICE_UP"; 96 97 /** 98 * Intent action sent when there is a new incoming SIP call. 99 * @hide 100 */ 101 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) 102 @SystemApi 103 public static final String ACTION_SIP_INCOMING_CALL = 104 "android.net.sip.action.SIP_INCOMING_CALL"; 105 106 /** 107 * Action string for the add-phone intent. 108 * Internal use only. 109 * @hide 110 */ 111 public static final String ACTION_SIP_ADD_PHONE = 112 "com.android.phone.SIP_ADD_PHONE"; 113 114 /** 115 * Intent action sent when a SIP profile has been removed. 116 * @hide 117 */ 118 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) 119 @SystemApi 120 public static final String ACTION_SIP_REMOVE_PROFILE = 121 "android.net.sip.action.SIP_REMOVE_PROFILE"; 122 123 /** 124 * Intent action sent when the SIP accounts or other configuration has changed. 125 * This should trigger a re-registration of the SIP PhoneAccounts. 126 * @hide 127 */ 128 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) 129 @SystemApi 130 public static final String ACTION_SIP_CALL_OPTION_CHANGED = 131 "android.net.sip.action.SIP_CALL_OPTION_CHANGED"; 132 133 /** 134 * Intent action used by Telephony to start the SIP service after about. 135 * @hide 136 */ 137 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) 138 @SystemApi 139 public static final String ACTION_START_SIP = 140 "android.net.sip.action.START_SIP"; 141 142 /** 143 * Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents. 144 * Internal use only. 145 * @hide 146 */ 147 public static final String EXTRA_LOCAL_URI = "android:localSipUri"; 148 149 private static final String TAG = "SipManager"; 150 151 private ISipService mSipService; 152 private Context mContext; 153 154 /** 155 * Creates a manager instance. Returns null if SIP API is not supported. 156 * 157 * @param context application context for creating the manager object 158 * @return the manager instance or null if SIP API is not supported 159 */ newInstance(Context context)160 public static SipManager newInstance(Context context) { 161 return (isApiSupported(context) ? new SipManager(context) : null); 162 } 163 164 /** 165 * Returns true if the SIP API is supported by the system. 166 */ isApiSupported(Context context)167 public static boolean isApiSupported(Context context) { 168 return context.getPackageManager().hasSystemFeature( 169 PackageManager.FEATURE_SIP); 170 } 171 172 /** 173 * Returns true if the system supports SIP-based VOIP API. 174 */ isVoipSupported(Context context)175 public static boolean isVoipSupported(Context context) { 176 return context.getPackageManager().hasSystemFeature( 177 PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context); 178 } 179 180 /** 181 * Returns true if SIP is only available on WIFI. 182 */ isSipWifiOnly(Context context)183 public static boolean isSipWifiOnly(Context context) { 184 return context.getResources().getBoolean( 185 com.android.internal.R.bool.config_sip_wifi_only); 186 } 187 SipManager(Context context)188 private SipManager(Context context) { 189 mContext = context; 190 createSipService(); 191 } 192 createSipService()193 private void createSipService() { 194 if (mSipService == null) { 195 IBinder b = ServiceManager.getService(Context.SIP_SERVICE); 196 mSipService = ISipService.Stub.asInterface(b); 197 } 198 } 199 checkSipServiceConnection()200 private void checkSipServiceConnection() throws SipException { 201 createSipService(); 202 if (mSipService == null) { 203 throw new SipException("SipService is dead and is restarting...", new Exception()); 204 } 205 } 206 207 /** 208 * Opens the profile for making generic SIP calls. The caller may make subsequent calls 209 * through {@link #makeAudioCall}. If one also wants to receive calls on the 210 * profile, use 211 * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)} 212 * instead. 213 * 214 * @param localProfile the SIP profile to make calls from 215 * @throws SipException if the profile contains incorrect settings or 216 * calling the SIP service results in an error 217 */ open(SipProfile localProfile)218 public void open(SipProfile localProfile) throws SipException { 219 try { 220 checkSipServiceConnection(); 221 mSipService.open(localProfile, mContext.getOpPackageName()); 222 } catch (RemoteException e) { 223 throw new SipException("open()", e); 224 } 225 } 226 227 /** 228 * Opens the profile for making calls and/or receiving generic SIP calls. The caller may 229 * make subsequent calls through {@link #makeAudioCall}. If the 230 * auto-registration option is enabled in the profile, the SIP service 231 * will register the profile to the corresponding SIP provider periodically 232 * in order to receive calls from the provider. When the SIP service 233 * receives a new call, it will send out an intent with the provided action 234 * string. The intent contains a call ID extra and an offer session 235 * description string extra. Use {@link #getCallId} and 236 * {@link #getOfferSessionDescription} to retrieve those extras. 237 * 238 * @param localProfile the SIP profile to receive incoming calls for 239 * @param incomingCallPendingIntent When an incoming call is received, the 240 * SIP service will call 241 * {@link PendingIntent#send(Context, int, Intent)} to send back the 242 * intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} as the 243 * result code and the intent to fill in the call ID and session 244 * description information. It cannot be null. 245 * @param listener to listen to registration events; can be null 246 * @see #getCallId 247 * @see #getOfferSessionDescription 248 * @see #takeAudioCall 249 * @throws NullPointerException if {@code incomingCallPendingIntent} is null 250 * @throws SipException if the profile contains incorrect settings or 251 * calling the SIP service results in an error 252 * @see #isIncomingCallIntent 253 * @see #getCallId 254 * @see #getOfferSessionDescription 255 */ open(SipProfile localProfile, PendingIntent incomingCallPendingIntent, SipRegistrationListener listener)256 public void open(SipProfile localProfile, 257 PendingIntent incomingCallPendingIntent, 258 SipRegistrationListener listener) throws SipException { 259 if (incomingCallPendingIntent == null) { 260 throw new NullPointerException( 261 "incomingCallPendingIntent cannot be null"); 262 } 263 try { 264 checkSipServiceConnection(); 265 mSipService.open3(localProfile, incomingCallPendingIntent, 266 createRelay(listener, localProfile.getUriString()), 267 mContext.getOpPackageName()); 268 } catch (RemoteException e) { 269 throw new SipException("open()", e); 270 } 271 } 272 273 /** 274 * Sets the listener to listen to registration events. No effect if the 275 * profile has not been opened to receive calls (see 276 * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}). 277 * 278 * @param localProfileUri the URI of the profile 279 * @param listener to listen to registration events; can be null 280 * @throws SipException if calling the SIP service results in an error 281 */ setRegistrationListener(String localProfileUri, SipRegistrationListener listener)282 public void setRegistrationListener(String localProfileUri, 283 SipRegistrationListener listener) throws SipException { 284 try { 285 checkSipServiceConnection(); 286 mSipService.setRegistrationListener( 287 localProfileUri, createRelay(listener, localProfileUri), 288 mContext.getOpPackageName()); 289 } catch (RemoteException e) { 290 throw new SipException("setRegistrationListener()", e); 291 } 292 } 293 294 /** 295 * Closes the specified profile to not make/receive calls. All the resources 296 * that were allocated to the profile are also released. 297 * 298 * @param localProfileUri the URI of the profile to close 299 * @throws SipException if calling the SIP service results in an error 300 */ close(String localProfileUri)301 public void close(String localProfileUri) throws SipException { 302 try { 303 checkSipServiceConnection(); 304 mSipService.close(localProfileUri, mContext.getOpPackageName()); 305 } catch (RemoteException e) { 306 throw new SipException("close()", e); 307 } 308 } 309 310 /** 311 * Checks if the specified profile is opened in the SIP service for 312 * making and/or receiving calls. 313 * 314 * @param localProfileUri the URI of the profile in question 315 * @return true if the profile is enabled to receive calls 316 * @throws SipException if calling the SIP service results in an error 317 */ isOpened(String localProfileUri)318 public boolean isOpened(String localProfileUri) throws SipException { 319 try { 320 checkSipServiceConnection(); 321 return mSipService.isOpened(localProfileUri, mContext.getOpPackageName()); 322 } catch (RemoteException e) { 323 throw new SipException("isOpened()", e); 324 } 325 } 326 327 /** 328 * Checks if the SIP service has successfully registered the profile to the 329 * SIP provider (specified in the profile) for receiving calls. Returning 330 * true from this method also implies the profile is opened 331 * ({@link #isOpened}). 332 * 333 * @param localProfileUri the URI of the profile in question 334 * @return true if the profile is registered to the SIP provider; false if 335 * the profile has not been opened in the SIP service or the SIP 336 * service has not yet successfully registered the profile to the SIP 337 * provider 338 * @throws SipException if calling the SIP service results in an error 339 */ isRegistered(String localProfileUri)340 public boolean isRegistered(String localProfileUri) throws SipException { 341 try { 342 checkSipServiceConnection(); 343 return mSipService.isRegistered(localProfileUri, mContext.getOpPackageName()); 344 } catch (RemoteException e) { 345 throw new SipException("isRegistered()", e); 346 } 347 } 348 349 /** 350 * Creates a {@link SipAudioCall} to make a call. The attempt will be timed 351 * out if the call is not established within {@code timeout} seconds and 352 * {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} 353 * will be called. 354 * 355 * @param localProfile the SIP profile to make the call from 356 * @param peerProfile the SIP profile to make the call to 357 * @param listener to listen to the call events from {@link SipAudioCall}; 358 * can be null 359 * @param timeout the timeout value in seconds. Default value (defined by 360 * SIP protocol) is used if {@code timeout} is zero or negative. 361 * @return a {@link SipAudioCall} object 362 * @throws SipException if calling the SIP service results in an error or 363 * VOIP API is not supported by the device 364 * @see SipAudioCall.Listener#onError 365 * @see #isVoipSupported 366 */ makeAudioCall(SipProfile localProfile, SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)367 public SipAudioCall makeAudioCall(SipProfile localProfile, 368 SipProfile peerProfile, SipAudioCall.Listener listener, int timeout) 369 throws SipException { 370 if (!isVoipSupported(mContext)) { 371 throw new SipException("VOIP API is not supported"); 372 } 373 SipAudioCall call = new SipAudioCall(mContext, localProfile); 374 call.setListener(listener); 375 SipSession s = createSipSession(localProfile, null); 376 call.makeCall(peerProfile, s, timeout); 377 return call; 378 } 379 380 /** 381 * Creates a {@link SipAudioCall} to make an audio call. The attempt will be 382 * timed out if the call is not established within {@code timeout} seconds 383 * and 384 * {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} 385 * will be called. 386 * 387 * @param localProfileUri URI of the SIP profile to make the call from 388 * @param peerProfileUri URI of the SIP profile to make the call to 389 * @param listener to listen to the call events from {@link SipAudioCall}; 390 * can be null 391 * @param timeout the timeout value in seconds. Default value (defined by 392 * SIP protocol) is used if {@code timeout} is zero or negative. 393 * @return a {@link SipAudioCall} object 394 * @throws SipException if calling the SIP service results in an error or 395 * VOIP API is not supported by the device 396 * @see SipAudioCall.Listener#onError 397 * @see #isVoipSupported 398 */ makeAudioCall(String localProfileUri, String peerProfileUri, SipAudioCall.Listener listener, int timeout)399 public SipAudioCall makeAudioCall(String localProfileUri, 400 String peerProfileUri, SipAudioCall.Listener listener, int timeout) 401 throws SipException { 402 if (!isVoipSupported(mContext)) { 403 throw new SipException("VOIP API is not supported"); 404 } 405 try { 406 return makeAudioCall( 407 new SipProfile.Builder(localProfileUri).build(), 408 new SipProfile.Builder(peerProfileUri).build(), listener, 409 timeout); 410 } catch (ParseException e) { 411 throw new SipException("build SipProfile", e); 412 } 413 } 414 415 /** 416 * Creates a {@link SipAudioCall} to take an incoming call. Before the call 417 * is returned, the listener will receive a 418 * {@link SipAudioCall.Listener#onRinging} 419 * callback. 420 * 421 * @param incomingCallIntent the incoming call broadcast intent 422 * @param listener to listen to the call events from {@link SipAudioCall}; 423 * can be null 424 * @return a {@link SipAudioCall} object 425 * @throws SipException if calling the SIP service results in an error 426 */ takeAudioCall(Intent incomingCallIntent, SipAudioCall.Listener listener)427 public SipAudioCall takeAudioCall(Intent incomingCallIntent, 428 SipAudioCall.Listener listener) throws SipException { 429 if (incomingCallIntent == null) { 430 throw new SipException("Cannot retrieve session with null intent"); 431 } 432 433 String callId = getCallId(incomingCallIntent); 434 if (callId == null) { 435 throw new SipException("Call ID missing in incoming call intent"); 436 } 437 438 String offerSd = getOfferSessionDescription(incomingCallIntent); 439 if (offerSd == null) { 440 throw new SipException("Session description missing in incoming " 441 + "call intent"); 442 } 443 444 try { 445 checkSipServiceConnection(); 446 ISipSession session = mSipService.getPendingSession(callId, 447 mContext.getOpPackageName()); 448 if (session == null) { 449 throw new SipException("No pending session for the call"); 450 } 451 SipAudioCall call = new SipAudioCall( 452 mContext, session.getLocalProfile()); 453 call.attachCall(new SipSession(session), offerSd); 454 call.setListener(listener); 455 return call; 456 } catch (Throwable t) { 457 throw new SipException("takeAudioCall()", t); 458 } 459 } 460 461 /** 462 * Checks if the intent is an incoming call broadcast intent. 463 * 464 * @param intent the intent in question 465 * @return true if the intent is an incoming call broadcast intent 466 */ isIncomingCallIntent(Intent intent)467 public static boolean isIncomingCallIntent(Intent intent) { 468 if (intent == null) return false; 469 String callId = getCallId(intent); 470 String offerSd = getOfferSessionDescription(intent); 471 return ((callId != null) && (offerSd != null)); 472 } 473 474 /** 475 * Gets the call ID from the specified incoming call broadcast intent. 476 * 477 * @param incomingCallIntent the incoming call broadcast intent 478 * @return the call ID or null if the intent does not contain it 479 */ getCallId(Intent incomingCallIntent)480 public static String getCallId(Intent incomingCallIntent) { 481 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID); 482 } 483 484 /** 485 * Gets the offer session description from the specified incoming call 486 * broadcast intent. 487 * 488 * @param incomingCallIntent the incoming call broadcast intent 489 * @return the offer session description or null if the intent does not 490 * have it 491 */ getOfferSessionDescription(Intent incomingCallIntent)492 public static String getOfferSessionDescription(Intent incomingCallIntent) { 493 return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD); 494 } 495 496 /** 497 * Creates an incoming call broadcast intent. 498 * 499 * @param callId the call ID of the incoming call 500 * @param sessionDescription the session description of the incoming call 501 * @return the incoming call intent 502 * @hide 503 */ createIncomingCallBroadcast(String callId, String sessionDescription)504 public static Intent createIncomingCallBroadcast(String callId, 505 String sessionDescription) { 506 Intent intent = new Intent(); 507 intent.putExtra(EXTRA_CALL_ID, callId); 508 intent.putExtra(EXTRA_OFFER_SD, sessionDescription); 509 return intent; 510 } 511 512 /** 513 * Manually registers the profile to the corresponding SIP provider for 514 * receiving calls. 515 * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)} is 516 * still needed to be called at least once in order for the SIP service to 517 * notify the caller with the {@link android.app.PendingIntent} when an incoming call is 518 * received. 519 * 520 * @param localProfile the SIP profile to register with 521 * @param expiryTime registration expiration time (in seconds) 522 * @param listener to listen to the registration events 523 * @throws SipException if calling the SIP service results in an error 524 */ register(SipProfile localProfile, int expiryTime, SipRegistrationListener listener)525 public void register(SipProfile localProfile, int expiryTime, 526 SipRegistrationListener listener) throws SipException { 527 try { 528 checkSipServiceConnection(); 529 ISipSession session = mSipService.createSession(localProfile, 530 createRelay(listener, localProfile.getUriString()), 531 mContext.getOpPackageName()); 532 if (session == null) { 533 throw new SipException( 534 "SipService.createSession() returns null"); 535 } 536 session.register(expiryTime); 537 } catch (RemoteException e) { 538 throw new SipException("register()", e); 539 } 540 } 541 542 /** 543 * Manually unregisters the profile from the corresponding SIP provider for 544 * stop receiving further calls. This may interference with the auto 545 * registration process in the SIP service if the auto-registration option 546 * in the profile is enabled. 547 * 548 * @param localProfile the SIP profile to register with 549 * @param listener to listen to the registration events 550 * @throws SipException if calling the SIP service results in an error 551 */ unregister(SipProfile localProfile, SipRegistrationListener listener)552 public void unregister(SipProfile localProfile, 553 SipRegistrationListener listener) throws SipException { 554 try { 555 checkSipServiceConnection(); 556 ISipSession session = mSipService.createSession(localProfile, 557 createRelay(listener, localProfile.getUriString()), 558 mContext.getOpPackageName()); 559 if (session == null) { 560 throw new SipException( 561 "SipService.createSession() returns null"); 562 } 563 session.unregister(); 564 } catch (RemoteException e) { 565 throw new SipException("unregister()", e); 566 } 567 } 568 569 /** 570 * Gets the {@link SipSession} that handles the incoming call. For audio 571 * calls, consider to use {@link SipAudioCall} to handle the incoming call. 572 * See {@link #takeAudioCall}. Note that the method may be called only once 573 * for the same intent. For subsequent calls on the same intent, the method 574 * returns null. 575 * 576 * @param incomingCallIntent the incoming call broadcast intent 577 * @return the session object that handles the incoming call 578 */ getSessionFor(Intent incomingCallIntent)579 public SipSession getSessionFor(Intent incomingCallIntent) 580 throws SipException { 581 try { 582 checkSipServiceConnection(); 583 String callId = getCallId(incomingCallIntent); 584 ISipSession s = mSipService.getPendingSession(callId, 585 mContext.getOpPackageName()); 586 return ((s == null) ? null : new SipSession(s)); 587 } catch (RemoteException e) { 588 throw new SipException("getSessionFor()", e); 589 } 590 } 591 createRelay( SipRegistrationListener listener, String uri)592 private static ISipSessionListener createRelay( 593 SipRegistrationListener listener, String uri) { 594 return ((listener == null) ? null : new ListenerRelay(listener, uri)); 595 } 596 597 /** 598 * Creates a {@link SipSession} with the specified profile. Use other 599 * methods, if applicable, instead of interacting with {@link SipSession} 600 * directly. 601 * 602 * @param localProfile the SIP profile the session is associated with 603 * @param listener to listen to SIP session events 604 */ createSipSession(SipProfile localProfile, SipSession.Listener listener)605 public SipSession createSipSession(SipProfile localProfile, 606 SipSession.Listener listener) throws SipException { 607 try { 608 checkSipServiceConnection(); 609 ISipSession s = mSipService.createSession(localProfile, null, 610 mContext.getOpPackageName()); 611 if (s == null) { 612 throw new SipException( 613 "Failed to create SipSession; network unavailable?"); 614 } 615 return new SipSession(s, listener); 616 } catch (RemoteException e) { 617 throw new SipException("createSipSession()", e); 618 } 619 } 620 621 /** 622 * Gets the list of profiles hosted by the SIP service. The user information 623 * (username, password and display name) are crossed out. 624 * @hide 625 */ 626 @SystemApi getProfiles()627 public @NonNull List<SipProfile> getProfiles() throws SipException { 628 try { 629 checkSipServiceConnection(); 630 return mSipService.getProfiles(mContext.getOpPackageName()); 631 } catch (RemoteException e) { 632 throw new SipException(e.getMessage()); 633 } 634 } 635 636 private static class ListenerRelay extends SipSessionAdapter { 637 private SipRegistrationListener mListener; 638 private String mUri; 639 640 // listener must not be null ListenerRelay(SipRegistrationListener listener, String uri)641 public ListenerRelay(SipRegistrationListener listener, String uri) { 642 mListener = listener; 643 mUri = uri; 644 } 645 getUri(ISipSession session)646 private String getUri(ISipSession session) { 647 try { 648 return ((session == null) 649 ? mUri 650 : session.getLocalProfile().getUriString()); 651 } catch (Throwable e) { 652 // SipService died? SIP stack died? 653 Rlog.e(TAG, "getUri(): ", e); 654 return null; 655 } 656 } 657 658 @Override onRegistering(ISipSession session)659 public void onRegistering(ISipSession session) { 660 mListener.onRegistering(getUri(session)); 661 } 662 663 @Override onRegistrationDone(ISipSession session, int duration)664 public void onRegistrationDone(ISipSession session, int duration) { 665 long expiryTime = duration; 666 if (duration > 0) expiryTime += System.currentTimeMillis(); 667 mListener.onRegistrationDone(getUri(session), expiryTime); 668 } 669 670 @Override onRegistrationFailed(ISipSession session, int errorCode, String message)671 public void onRegistrationFailed(ISipSession session, int errorCode, 672 String message) { 673 mListener.onRegistrationFailed(getUri(session), errorCode, message); 674 } 675 676 @Override onRegistrationTimeout(ISipSession session)677 public void onRegistrationTimeout(ISipSession session) { 678 mListener.onRegistrationFailed(getUri(session), 679 SipErrorCode.TIME_OUT, "registration timed out"); 680 } 681 } 682 } 683