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