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