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