SipManager.java revision 08faac3c26e12863858e1534985dd950193f755f
19f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu/* 29f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * Copyright (C) 2010 The Android Open Source Project 39f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * 49f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * Licensed under the Apache License, Version 2.0 (the "License"); 59f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * you may not use this file except in compliance with the License. 69f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * You may obtain a copy of the License at 79f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * 89f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * http://www.apache.org/licenses/LICENSE-2.0 99f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * 109f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * Unless required by applicable law or agreed to in writing, software 119f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * distributed under the License is distributed on an "AS IS" BASIS, 129f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * See the License for the specific language governing permissions and 149f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * limitations under the License. 159f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu */ 169f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu 179f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapupackage android.net.sip; 189f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu 199f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.app.PendingIntent; 209f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.content.Context; 219f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.content.Intent; 229f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.content.pm.PackageManager; 239f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.os.IBinder; 249f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.os.Looper; 259f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.os.RemoteException; 269f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.os.ServiceManager; 279f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapuimport android.util.Log; 28ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu 29ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapuimport java.text.ParseException; 309f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu 31ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu/** 32ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * The class provides API for various SIP related tasks. Specifically, the API 33ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * allows an application to: 34ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * <ul> 35ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * <li>open a {@link SipProfile} to get ready for making outbound calls or have 369f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * the background SIP service listen to incoming calls and broadcast them 379f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * with registered command string. See 389f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}, 399f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and 409f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * {@link #isRegistered}. It also facilitates handling of the incoming call 419f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * broadcast intent. See 429f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * {@link #isIncomingCallIntent}, {@link #getCallId}, 439f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li> 44ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * <li>make/take SIP-based audio calls. See 45ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * {@link #makeAudioCall} and {@link #takeAudioCall}.</li> 46ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * <li>register/unregister with a SIP service provider manually. See 47ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * {@link #register} and {@link #unregister}.</li> 48ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * <li>process SIP events directly with a {@link SipSession} created by 49ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * {@link #createSipSession}.</li> 50ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * </ul> 51ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * {@code SipManager} can only be instantiated if SIP API is supported by the 52ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * device. (See {@link #isApiSupported}). 53ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu */ 54ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapupublic class SipManager { 55ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 56ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * The result code to be sent back with the incoming call 57ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * {@link PendingIntent}. 58ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * @see #open(SipProfile, PendingIntent, SipRegistrationListener) 59ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu */ 60ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu public static final int INCOMING_CALL_RESULT_CODE = 101; 61ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu 629f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu /** 639f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * Key to retrieve the call ID from an incoming call intent. 649f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * @see #open(SipProfile, PendingIntent, SipRegistrationListener) 659f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu */ 66ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu public static final String EXTRA_CALL_ID = "android:sipCallID"; 67ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu 68ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 69ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Key to retrieve the offered session description from an incoming call 70ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * intent. 71ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * @see #open(SipProfile, PendingIntent, SipRegistrationListener) 72ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu */ 73ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu public static final String EXTRA_OFFER_SD = "android:sipOfferSD"; 74ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu 75ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 76ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Action to broadcast when SipService is up. 779f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * Internal use only. 789f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * @hide 799f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu */ 809f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu public static final String ACTION_SIP_SERVICE_UP = 81ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu "android.net.sip.SIP_SERVICE_UP"; 82ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 83ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Action string for the incoming call intent for the Phone app. 84ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Internal use only. 85ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * @hide 86ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu */ 87ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu public static final String ACTION_SIP_INCOMING_CALL = 88ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu "com.android.phone.SIP_INCOMING_CALL"; 89ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 90ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Action string for the add-phone intent. 91ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Internal use only. 929f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * @hide 939f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu */ 949f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu public static final String ACTION_SIP_ADD_PHONE = 959f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu "com.android.phone.SIP_ADD_PHONE"; 96ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 97ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Action string for the remove-phone intent. 98ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Internal use only. 99ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * @hide 100ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu */ 101ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu public static final String ACTION_SIP_REMOVE_PHONE = 102ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu "com.android.phone.SIP_REMOVE_PHONE"; 103ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 104ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents. 105ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Internal use only. 106ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * @hide 1079f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu */ 1089f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu public static final String EXTRA_LOCAL_URI = "android:localSipUri"; 1099f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu 1109f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu private static final String TAG = "SipManager"; 111ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu 112ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu private ISipService mSipService; 113ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu private Context mContext; 114ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu 115ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 116ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Creates a manager instance. Returns null if SIP API is not supported. 117ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * 118ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * @param context application context for creating the manager object 119ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * @return the manager instance or null if SIP API is not supported 120ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu */ 121ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu public static SipManager newInstance(Context context) { 1229f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu return (isApiSupported(context) ? new SipManager(context) : null); 1239f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu } 1249f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu 1259f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu /** 126ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu * Returns true if the SIP API is supported by the system. 127ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu */ 128ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu public static boolean isApiSupported(Context context) { 129ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu return true; 130ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /* TODO: uncomment this before ship 131ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu return context.getPackageManager().hasSystemFeature( 132ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu PackageManager.FEATURE_SIP); 133ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu */ 134ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu } 135ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu 136ea196fc50e9711207eddb1f546fec1463b615fc0Suchi Amalapurapu /** 1379f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu * Returns true if the system supports SIP-based VoIP. 1389f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu */ 1399f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu public static boolean isVoipSupported(Context context) { 1409f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu return true; 1419f2f87d92b3c23680fa7c24dbccf67976d0968d3Suchi Amalapurapu /* TODO: uncomment this before ship 142 return context.getPackageManager().hasSystemFeature( 143 PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context); 144 */ 145 } 146 147 /** 148 * Returns true if SIP is only available on WIFI. 149 */ 150 public static boolean isSipWifiOnly(Context context) { 151 return context.getResources().getBoolean( 152 com.android.internal.R.bool.config_sip_wifi_only); 153 } 154 155 private SipManager(Context context) { 156 mContext = context; 157 createSipService(); 158 } 159 160 private void createSipService() { 161 IBinder b = ServiceManager.getService(Context.SIP_SERVICE); 162 mSipService = ISipService.Stub.asInterface(b); 163 } 164 165 /** 166 * Opens the profile for making calls. The caller may make subsequent calls 167 * through {@link #makeAudioCall}. If one also wants to receive calls on the 168 * profile, use 169 * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)} 170 * instead. 171 * 172 * @param localProfile the SIP profile to make calls from 173 * @throws SipException if the profile contains incorrect settings or 174 * calling the SIP service results in an error 175 */ 176 public void open(SipProfile localProfile) throws SipException { 177 try { 178 mSipService.open(localProfile); 179 } catch (RemoteException e) { 180 throw new SipException("open()", e); 181 } 182 } 183 184 /** 185 * Opens the profile for making calls and/or receiving calls. The caller may 186 * make subsequent calls through {@link #makeAudioCall}. If the 187 * auto-registration option is enabled in the profile, the SIP service 188 * will register the profile to the corresponding SIP provider periodically 189 * in order to receive calls from the provider. When the SIP service 190 * receives a new call, it will send out an intent with the provided action 191 * string. The intent contains a call ID extra and an offer session 192 * description string extra. Use {@link #getCallId} and 193 * {@link #getOfferSessionDescription} to retrieve those extras. 194 * 195 * @param localProfile the SIP profile to receive incoming calls for 196 * @param incomingCallPendingIntent When an incoming call is received, the 197 * SIP service will call 198 * {@link PendingIntent#send(Context, int, Intent)} to send back the 199 * intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} as the 200 * result code and the intent to fill in the call ID and session 201 * description information. It cannot be null. 202 * @param listener to listen to registration events; can be null 203 * @see #getCallId 204 * @see #getOfferSessionDescription 205 * @see #takeAudioCall 206 * @throws NullPointerException if {@code incomingCallPendingIntent} is null 207 * @throws SipException if the profile contains incorrect settings or 208 * calling the SIP service results in an error 209 * @see #isIncomingCallIntent 210 * @see #getCallId 211 * @see #getOfferSessionDescription 212 */ 213 public void open(SipProfile localProfile, 214 PendingIntent incomingCallPendingIntent, 215 SipRegistrationListener listener) throws SipException { 216 if (incomingCallPendingIntent == null) { 217 throw new NullPointerException( 218 "incomingCallPendingIntent cannot be null"); 219 } 220 try { 221 mSipService.open3(localProfile, incomingCallPendingIntent, 222 createRelay(listener, localProfile.getUriString())); 223 } catch (RemoteException e) { 224 throw new SipException("open()", e); 225 } 226 } 227 228 /** 229 * Sets the listener to listen to registration events. No effect if the 230 * profile has not been opened to receive calls (see 231 * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}). 232 * 233 * @param localProfileUri the URI of the profile 234 * @param listener to listen to registration events; can be null 235 * @throws SipException if calling the SIP service results in an error 236 */ 237 public void setRegistrationListener(String localProfileUri, 238 SipRegistrationListener listener) throws SipException { 239 try { 240 mSipService.setRegistrationListener( 241 localProfileUri, createRelay(listener, localProfileUri)); 242 } catch (RemoteException e) { 243 throw new SipException("setRegistrationListener()", e); 244 } 245 } 246 247 /** 248 * Closes the specified profile to not make/receive calls. All the resources 249 * that were allocated to the profile are also released. 250 * 251 * @param localProfileUri the URI of the profile to close 252 * @throws SipException if calling the SIP service results in an error 253 */ 254 public void close(String localProfileUri) throws SipException { 255 try { 256 mSipService.close(localProfileUri); 257 } catch (RemoteException e) { 258 throw new SipException("close()", e); 259 } 260 } 261 262 /** 263 * Checks if the specified profile is opened in the SIP service for 264 * making and/or receiving calls. 265 * 266 * @param localProfileUri the URI of the profile in question 267 * @return true if the profile is enabled to receive calls 268 * @throws SipException if calling the SIP service results in an error 269 */ 270 public boolean isOpened(String localProfileUri) throws SipException { 271 try { 272 return mSipService.isOpened(localProfileUri); 273 } catch (RemoteException e) { 274 throw new SipException("isOpened()", e); 275 } 276 } 277 278 /** 279 * Checks if the SIP service has successfully registered the profile to the 280 * SIP provider (specified in the profile) for receiving calls. Returning 281 * true from this method also implies the profile is opened 282 * ({@link #isOpened}). 283 * 284 * @param localProfileUri the URI of the profile in question 285 * @return true if the profile is registered to the SIP provider; false if 286 * the profile has not been opened in the SIP service or the SIP 287 * service has not yet successfully registered the profile to the SIP 288 * provider 289 * @throws SipException if calling the SIP service results in an error 290 */ 291 public boolean isRegistered(String localProfileUri) throws SipException { 292 try { 293 return mSipService.isRegistered(localProfileUri); 294 } catch (RemoteException e) { 295 throw new SipException("isRegistered()", e); 296 } 297 } 298 299 /** 300 * Creates a {@link SipAudioCall} to make a call. The attempt will be timed 301 * out if the call is not established within {@code timeout} seconds and 302 * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} 303 * will be called. 304 * 305 * @param localProfile the SIP profile to make the call from 306 * @param peerProfile the SIP profile to make the call to 307 * @param listener to listen to the call events from {@link SipAudioCall}; 308 * can be null 309 * @param timeout the timeout value in seconds. Default value (defined by 310 * SIP protocol) is used if {@code timeout} is zero or negative. 311 * @return a {@link SipAudioCall} object 312 * @throws SipException if calling the SIP service results in an error 313 * @see SipAudioCall.Listener.onError 314 */ 315 public SipAudioCall makeAudioCall(SipProfile localProfile, 316 SipProfile peerProfile, SipAudioCall.Listener listener, int timeout) 317 throws SipException { 318 SipAudioCall call = new SipAudioCall(mContext, localProfile); 319 call.setListener(listener); 320 SipSession s = createSipSession(localProfile, null); 321 if (s == null) { 322 throw new SipException( 323 "Failed to create SipSession; network available?"); 324 } 325 call.makeCall(peerProfile, s, timeout); 326 return call; 327 } 328 329 /** 330 * Creates a {@link SipAudioCall} to make an audio call. The attempt will be 331 * timed out if the call is not established within {@code timeout} seconds 332 * and 333 * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} 334 * will be called. 335 * 336 * @param localProfileUri URI of the SIP profile to make the call from 337 * @param peerProfileUri URI of the SIP profile to make the call to 338 * @param listener to listen to the call events from {@link SipAudioCall}; 339 * can be null 340 * @param timeout the timeout value in seconds. Default value (defined by 341 * SIP protocol) is used if {@code timeout} is zero or negative. 342 * @return a {@link SipAudioCall} object 343 * @throws SipException if calling the SIP service results in an error 344 * @see SipAudioCall.Listener.onError 345 */ 346 public SipAudioCall makeAudioCall(String localProfileUri, 347 String peerProfileUri, SipAudioCall.Listener listener, int timeout) 348 throws SipException { 349 try { 350 return makeAudioCall( 351 new SipProfile.Builder(localProfileUri).build(), 352 new SipProfile.Builder(peerProfileUri).build(), listener, 353 timeout); 354 } catch (ParseException e) { 355 throw new SipException("build SipProfile", e); 356 } 357 } 358 359 /** 360 * The method calls {@code takeAudioCall(incomingCallIntent, 361 * listener, true}. 362 * 363 * @see #takeAudioCall(Intent, SipAudioCall.Listener, boolean) 364 */ 365 public SipAudioCall takeAudioCall(Intent incomingCallIntent, 366 SipAudioCall.Listener listener) throws SipException { 367 return takeAudioCall(incomingCallIntent, listener, true); 368 } 369 370 /** 371 * Creates a {@link SipAudioCall} to take an incoming call. Before the call 372 * is returned, the listener will receive a 373 * {@link SipAudioCall.Listener#onRinging} 374 * callback. 375 * 376 * @param incomingCallIntent the incoming call broadcast intent 377 * @param listener to listen to the call events from {@link SipAudioCall}; 378 * can be null 379 * @return a {@link SipAudioCall} object 380 * @throws SipException if calling the SIP service results in an error 381 */ 382 public SipAudioCall takeAudioCall(Intent incomingCallIntent, 383 SipAudioCall.Listener listener, boolean ringtoneEnabled) 384 throws SipException { 385 if (incomingCallIntent == null) return null; 386 387 String callId = getCallId(incomingCallIntent); 388 if (callId == null) { 389 throw new SipException("Call ID missing in incoming call intent"); 390 } 391 392 String offerSd = getOfferSessionDescription(incomingCallIntent); 393 if (offerSd == null) { 394 throw new SipException("Session description missing in incoming " 395 + "call intent"); 396 } 397 398 try { 399 ISipSession session = mSipService.getPendingSession(callId); 400 if (session == null) return null; 401 SipAudioCall call = new SipAudioCall( 402 mContext, session.getLocalProfile()); 403 call.setRingtoneEnabled(ringtoneEnabled); 404 call.attachCall(new SipSession(session), offerSd); 405 call.setListener(listener); 406 return call; 407 } catch (Throwable t) { 408 throw new SipException("takeAudioCall()", t); 409 } 410 } 411 412 /** 413 * Checks if the intent is an incoming call broadcast intent. 414 * 415 * @param intent the intent in question 416 * @return true if the intent is an incoming call broadcast intent 417 */ 418 public static boolean isIncomingCallIntent(Intent intent) { 419 if (intent == null) return false; 420 String callId = getCallId(intent); 421 String offerSd = getOfferSessionDescription(intent); 422 return ((callId != null) && (offerSd != null)); 423 } 424 425 /** 426 * Gets the call ID from the specified incoming call broadcast intent. 427 * 428 * @param incomingCallIntent the incoming call broadcast intent 429 * @return the call ID or null if the intent does not contain it 430 */ 431 public static String getCallId(Intent incomingCallIntent) { 432 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID); 433 } 434 435 /** 436 * Gets the offer session description from the specified incoming call 437 * broadcast intent. 438 * 439 * @param incomingCallIntent the incoming call broadcast intent 440 * @return the offer session description or null if the intent does not 441 * have it 442 */ 443 public static String getOfferSessionDescription(Intent incomingCallIntent) { 444 return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD); 445 } 446 447 /** 448 * Creates an incoming call broadcast intent. 449 * 450 * @param callId the call ID of the incoming call 451 * @param sessionDescription the session description of the incoming call 452 * @return the incoming call intent 453 * @hide 454 */ 455 public static Intent createIncomingCallBroadcast(String callId, 456 String sessionDescription) { 457 Intent intent = new Intent(); 458 intent.putExtra(EXTRA_CALL_ID, callId); 459 intent.putExtra(EXTRA_OFFER_SD, sessionDescription); 460 return intent; 461 } 462 463 /** 464 * Manually registers the profile to the corresponding SIP provider for 465 * receiving calls. 466 * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)} is 467 * still needed to be called at least once in order for the SIP service to 468 * notify the caller with the {@code PendingIntent} when an incoming call is 469 * received. 470 * 471 * @param localProfile the SIP profile to register with 472 * @param expiryTime registration expiration time (in seconds) 473 * @param listener to listen to the registration events 474 * @throws SipException if calling the SIP service results in an error 475 */ 476 public void register(SipProfile localProfile, int expiryTime, 477 SipRegistrationListener listener) throws SipException { 478 try { 479 ISipSession session = mSipService.createSession(localProfile, 480 createRelay(listener, localProfile.getUriString())); 481 session.register(expiryTime); 482 } catch (RemoteException e) { 483 throw new SipException("register()", e); 484 } 485 } 486 487 /** 488 * Manually unregisters the profile from the corresponding SIP provider for 489 * stop receiving further calls. This may interference with the auto 490 * registration process in the SIP service if the auto-registration option 491 * in the profile is enabled. 492 * 493 * @param localProfile the SIP profile to register with 494 * @param listener to listen to the registration events 495 * @throws SipException if calling the SIP service results in an error 496 */ 497 public void unregister(SipProfile localProfile, 498 SipRegistrationListener listener) throws SipException { 499 try { 500 ISipSession session = mSipService.createSession(localProfile, 501 createRelay(listener, localProfile.getUriString())); 502 session.unregister(); 503 } catch (RemoteException e) { 504 throw new SipException("unregister()", e); 505 } 506 } 507 508 /** 509 * Gets the {@link SipSession} that handles the incoming call. For audio 510 * calls, consider to use {@link SipAudioCall} to handle the incoming call. 511 * See {@link #takeAudioCall}. Note that the method may be called only once 512 * for the same intent. For subsequent calls on the same intent, the method 513 * returns null. 514 * 515 * @param incomingCallIntent the incoming call broadcast intent 516 * @return the session object that handles the incoming call 517 */ 518 public SipSession getSessionFor(Intent incomingCallIntent) 519 throws SipException { 520 try { 521 String callId = getCallId(incomingCallIntent); 522 ISipSession s = mSipService.getPendingSession(callId); 523 return new SipSession(s); 524 } catch (RemoteException e) { 525 throw new SipException("getSessionFor()", e); 526 } 527 } 528 529 private static ISipSessionListener createRelay( 530 SipRegistrationListener listener, String uri) { 531 return ((listener == null) ? null : new ListenerRelay(listener, uri)); 532 } 533 534 /** 535 * Creates a {@link SipSession} with the specified profile. Use other 536 * methods, if applicable, instead of interacting with {@link SipSession} 537 * directly. 538 * 539 * @param localProfile the SIP profile the session is associated with 540 * @param listener to listen to SIP session events 541 */ 542 public SipSession createSipSession(SipProfile localProfile, 543 SipSession.Listener listener) throws SipException { 544 try { 545 ISipSession s = mSipService.createSession(localProfile, null); 546 return new SipSession(s, listener); 547 } catch (RemoteException e) { 548 throw new SipException("createSipSession()", e); 549 } 550 } 551 552 /** 553 * Gets the list of profiles hosted by the SIP service. The user information 554 * (username, password and display name) are crossed out. 555 * @hide 556 */ 557 public SipProfile[] getListOfProfiles() { 558 try { 559 return mSipService.getListOfProfiles(); 560 } catch (RemoteException e) { 561 return null; 562 } 563 } 564 565 private static class ListenerRelay extends SipSessionAdapter { 566 private SipRegistrationListener mListener; 567 private String mUri; 568 569 // listener must not be null 570 public ListenerRelay(SipRegistrationListener listener, String uri) { 571 mListener = listener; 572 mUri = uri; 573 } 574 575 private String getUri(ISipSession session) { 576 try { 577 return ((session == null) 578 ? mUri 579 : session.getLocalProfile().getUriString()); 580 } catch (Throwable e) { 581 // SipService died? SIP stack died? 582 Log.w(TAG, "getUri(): " + e); 583 return null; 584 } 585 } 586 587 @Override 588 public void onRegistering(ISipSession session) { 589 mListener.onRegistering(getUri(session)); 590 } 591 592 @Override 593 public void onRegistrationDone(ISipSession session, int duration) { 594 long expiryTime = duration; 595 if (duration > 0) expiryTime += System.currentTimeMillis(); 596 mListener.onRegistrationDone(getUri(session), expiryTime); 597 } 598 599 @Override 600 public void onRegistrationFailed(ISipSession session, int errorCode, 601 String message) { 602 mListener.onRegistrationFailed(getUri(session), errorCode, message); 603 } 604 605 @Override 606 public void onRegistrationTimeout(ISipSession session) { 607 mListener.onRegistrationFailed(getUri(session), 608 SipErrorCode.TIME_OUT, "registration timed out"); 609 } 610 } 611} 612