blob: 74c36725a70497ad83df4d3a8a33618f2bd2e942 [file] [log] [blame]
Chung-yih Wang363c2ab2010-08-05 10:21:20 +08001/*
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
17package android.net.sip;
18
Hung-ying Tyan323d3672010-10-05 09:35:38 +080019import android.app.PendingIntent;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080020import android.content.Context;
21import android.content.Intent;
Hung-ying Tyan3424c022010-08-27 18:08:19 +080022import android.content.pm.PackageManager;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080023import android.os.IBinder;
24import android.os.Looper;
25import android.os.RemoteException;
26import android.os.ServiceManager;
Hung-ying Tyan9ea96c62010-10-03 20:35:02 +080027import android.util.Log;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080028
29import java.text.ParseException;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080030
31/**
Scott Main02b1d682010-10-22 11:29:57 -070032 * Provides APIs for SIP tasks, such as initiating SIP connections, and provides access to related
33 * SIP services. This class is the starting point for any SIP actions. You can acquire an instance
34 * of it with {@link #newInstance newInstance()}.</p>
35 * <p>The APIs in this class allows you to:</p>
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080036 * <ul>
Scott Main02b1d682010-10-22 11:29:57 -070037 * <li>Create a {@link SipSession} to get ready for making calls or listen for incoming calls. See
38 * {@link #createSipSession createSipSession()} and {@link #getSessionFor getSessionFor()}.</li>
39 * <li>Initiate and receive generic SIP calls or audio-only SIP calls. Generic SIP calls may
40 * be video, audio, or other, and are initiated with {@link #open open()}. Audio-only SIP calls
41 * should be handled with a {@link SipAudioCall}, which you can acquire with {@link
42 * #makeAudioCall makeAudioCall()} and {@link #takeAudioCall takeAudioCall()}.</li>
43 * <li>Register and unregister with a SIP service provider, with
44 * {@link #register register()} and {@link #unregister unregister()}.</li>
45 * <li>Verify session connectivity, with {@link #isOpened isOpened()} and
46 * {@link #isRegistered isRegistered()}.</li>
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080047 * </ul>
Scott Main02b1d682010-10-22 11:29:57 -070048 * <p class="note"><strong>Note:</strong> Not all Android-powered devices support VOIP calls using
49 * SIP. You should always call {@link android.net.sip.SipManager#isVoipSupported
50 * isVoipSupported()} to verify that the device supports VOIP calling and {@link
51 * android.net.sip.SipManager#isApiSupported isApiSupported()} to verify that the device supports
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080052 * the SIP APIs. Your application must also request the {@link
Scott Main02b1d682010-10-22 11:29:57 -070053 * android.Manifest.permission#INTERNET} and {@link android.Manifest.permission#USE_SIP}
54 * permissions.</p>
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080055 *
56 * <div class="special reference">
57 * <h3>Developer Guides</h3>
58 * <p>For more information about using SIP, read the
59 * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
60 * developer guide.</p>
61 * </div>
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080062 */
63public class SipManager {
Hung-ying Tyan84a357b2010-09-16 04:11:32 +080064 /**
Hung-ying Tyan323d3672010-10-05 09:35:38 +080065 * The result code to be sent back with the incoming call
66 * {@link PendingIntent}.
67 * @see #open(SipProfile, PendingIntent, SipRegistrationListener)
68 */
69 public static final int INCOMING_CALL_RESULT_CODE = 101;
70
Hung-ying Tyan08faac32010-09-16 04:11:32 +080071 /**
72 * Key to retrieve the call ID from an incoming call intent.
73 * @see #open(SipProfile, PendingIntent, SipRegistrationListener)
74 */
Hung-ying Tyan323d3672010-10-05 09:35:38 +080075 public static final String EXTRA_CALL_ID = "android:sipCallID";
76
Hung-ying Tyan08faac32010-09-16 04:11:32 +080077 /**
78 * Key to retrieve the offered session description from an incoming call
79 * intent.
80 * @see #open(SipProfile, PendingIntent, SipRegistrationListener)
81 */
Hung-ying Tyan323d3672010-10-05 09:35:38 +080082 public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
83
84 /**
Hung-ying Tyan9db99a42010-10-07 09:14:57 +080085 * Action to broadcast when SipService is up.
86 * Internal use only.
87 * @hide
88 */
89 public static final String ACTION_SIP_SERVICE_UP =
90 "android.net.sip.SIP_SERVICE_UP";
91 /**
Hung-ying Tyan84a357b2010-09-16 04:11:32 +080092 * Action string for the incoming call intent for the Phone app.
93 * Internal use only.
94 * @hide
95 */
96 public static final String ACTION_SIP_INCOMING_CALL =
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080097 "com.android.phone.SIP_INCOMING_CALL";
Hung-ying Tyan84a357b2010-09-16 04:11:32 +080098 /**
99 * Action string for the add-phone intent.
100 * Internal use only.
101 * @hide
102 */
103 public static final String ACTION_SIP_ADD_PHONE =
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800104 "com.android.phone.SIP_ADD_PHONE";
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800105 /**
106 * Action string for the remove-phone intent.
107 * Internal use only.
108 * @hide
109 */
110 public static final String ACTION_SIP_REMOVE_PHONE =
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800111 "com.android.phone.SIP_REMOVE_PHONE";
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800112 /**
113 * Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents.
114 * Internal use only.
115 * @hide
116 */
117 public static final String EXTRA_LOCAL_URI = "android:localSipUri";
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800118
Hung-ying Tyan9ea96c62010-10-03 20:35:02 +0800119 private static final String TAG = "SipManager";
120
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800121 private ISipService mSipService;
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800122 private Context mContext;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800123
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800124 /**
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800125 * Creates a manager instance. Returns null if SIP API is not supported.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800126 *
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800127 * @param context application context for creating the manager object
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800128 * @return the manager instance or null if SIP API is not supported
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800129 */
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800130 public static SipManager newInstance(Context context) {
131 return (isApiSupported(context) ? new SipManager(context) : null);
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800132 }
133
134 /**
135 * Returns true if the SIP API is supported by the system.
136 */
137 public static boolean isApiSupported(Context context) {
138 return context.getPackageManager().hasSystemFeature(
139 PackageManager.FEATURE_SIP);
140 }
141
142 /**
Hung-ying Tyan5bd37822010-12-20 19:08:24 +0800143 * Returns true if the system supports SIP-based VOIP API.
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800144 */
145 public static boolean isVoipSupported(Context context) {
146 return context.getPackageManager().hasSystemFeature(
147 PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800148 }
149
Hung-ying Tyanc4b87472010-09-19 18:23:44 +0800150 /**
151 * Returns true if SIP is only available on WIFI.
152 */
153 public static boolean isSipWifiOnly(Context context) {
154 return context.getResources().getBoolean(
155 com.android.internal.R.bool.config_sip_wifi_only);
156 }
157
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800158 private SipManager(Context context) {
159 mContext = context;
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800160 createSipService();
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800161 }
162
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800163 private void createSipService() {
Chung-yih Wangcde66df2010-08-05 13:25:38 +0800164 IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800165 mSipService = ISipService.Stub.asInterface(b);
166 }
167
168 /**
Scott Main02b1d682010-10-22 11:29:57 -0700169 * Opens the profile for making generic SIP calls. The caller may make subsequent calls
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800170 * through {@link #makeAudioCall}. If one also wants to receive calls on the
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800171 * profile, use
172 * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800173 * instead.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800174 *
175 * @param localProfile the SIP profile to make calls from
176 * @throws SipException if the profile contains incorrect settings or
177 * calling the SIP service results in an error
178 */
179 public void open(SipProfile localProfile) throws SipException {
180 try {
181 mSipService.open(localProfile);
182 } catch (RemoteException e) {
183 throw new SipException("open()", e);
184 }
185 }
186
187 /**
Scott Main02b1d682010-10-22 11:29:57 -0700188 * Opens the profile for making calls and/or receiving generic SIP calls. The caller may
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800189 * make subsequent calls through {@link #makeAudioCall}. If the
190 * auto-registration option is enabled in the profile, the SIP service
191 * will register the profile to the corresponding SIP provider periodically
Hung-ying Tyan08faac32010-09-16 04:11:32 +0800192 * in order to receive calls from the provider. When the SIP service
193 * receives a new call, it will send out an intent with the provided action
194 * string. The intent contains a call ID extra and an offer session
195 * description string extra. Use {@link #getCallId} and
196 * {@link #getOfferSessionDescription} to retrieve those extras.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800197 *
198 * @param localProfile the SIP profile to receive incoming calls for
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800199 * @param incomingCallPendingIntent When an incoming call is received, the
200 * SIP service will call
201 * {@link PendingIntent#send(Context, int, Intent)} to send back the
202 * intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} as the
203 * result code and the intent to fill in the call ID and session
204 * description information. It cannot be null.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800205 * @param listener to listen to registration events; can be null
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800206 * @see #getCallId
207 * @see #getOfferSessionDescription
208 * @see #takeAudioCall
209 * @throws NullPointerException if {@code incomingCallPendingIntent} is null
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800210 * @throws SipException if the profile contains incorrect settings or
211 * calling the SIP service results in an error
Hung-ying Tyan08faac32010-09-16 04:11:32 +0800212 * @see #isIncomingCallIntent
213 * @see #getCallId
214 * @see #getOfferSessionDescription
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800215 */
216 public void open(SipProfile localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800217 PendingIntent incomingCallPendingIntent,
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800218 SipRegistrationListener listener) throws SipException {
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800219 if (incomingCallPendingIntent == null) {
220 throw new NullPointerException(
221 "incomingCallPendingIntent cannot be null");
222 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800223 try {
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800224 mSipService.open3(localProfile, incomingCallPendingIntent,
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800225 createRelay(listener, localProfile.getUriString()));
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800226 } catch (RemoteException e) {
227 throw new SipException("open()", e);
228 }
229 }
230
231 /**
232 * Sets the listener to listen to registration events. No effect if the
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800233 * profile has not been opened to receive calls (see
234 * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}).
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800235 *
236 * @param localProfileUri the URI of the profile
237 * @param listener to listen to registration events; can be null
238 * @throws SipException if calling the SIP service results in an error
239 */
240 public void setRegistrationListener(String localProfileUri,
241 SipRegistrationListener listener) throws SipException {
242 try {
243 mSipService.setRegistrationListener(
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800244 localProfileUri, createRelay(listener, localProfileUri));
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800245 } catch (RemoteException e) {
246 throw new SipException("setRegistrationListener()", e);
247 }
248 }
249
250 /**
251 * Closes the specified profile to not make/receive calls. All the resources
252 * that were allocated to the profile are also released.
253 *
254 * @param localProfileUri the URI of the profile to close
255 * @throws SipException if calling the SIP service results in an error
256 */
257 public void close(String localProfileUri) throws SipException {
258 try {
259 mSipService.close(localProfileUri);
260 } catch (RemoteException e) {
261 throw new SipException("close()", e);
262 }
263 }
264
265 /**
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800266 * Checks if the specified profile is opened in the SIP service for
267 * making and/or receiving calls.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800268 *
269 * @param localProfileUri the URI of the profile in question
270 * @return true if the profile is enabled to receive calls
271 * @throws SipException if calling the SIP service results in an error
272 */
273 public boolean isOpened(String localProfileUri) throws SipException {
274 try {
275 return mSipService.isOpened(localProfileUri);
276 } catch (RemoteException e) {
277 throw new SipException("isOpened()", e);
278 }
279 }
280
281 /**
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800282 * Checks if the SIP service has successfully registered the profile to the
283 * SIP provider (specified in the profile) for receiving calls. Returning
284 * true from this method also implies the profile is opened
285 * ({@link #isOpened}).
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800286 *
287 * @param localProfileUri the URI of the profile in question
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800288 * @return true if the profile is registered to the SIP provider; false if
289 * the profile has not been opened in the SIP service or the SIP
290 * service has not yet successfully registered the profile to the SIP
291 * provider
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800292 * @throws SipException if calling the SIP service results in an error
293 */
294 public boolean isRegistered(String localProfileUri) throws SipException {
295 try {
296 return mSipService.isRegistered(localProfileUri);
297 } catch (RemoteException e) {
298 throw new SipException("isRegistered()", e);
299 }
300 }
301
302 /**
Hung-ying Tyan9352cf12010-09-16 20:14:18 +0800303 * Creates a {@link SipAudioCall} to make a call. The attempt will be timed
304 * out if the call is not established within {@code timeout} seconds and
Scott Main02b1d682010-10-22 11:29:57 -0700305 * {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
Hung-ying Tyan9352cf12010-09-16 20:14:18 +0800306 * will be called.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800307 *
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800308 * @param localProfile the SIP profile to make the call from
309 * @param peerProfile the SIP profile to make the call to
310 * @param listener to listen to the call events from {@link SipAudioCall};
311 * can be null
Hung-ying Tyan08faac32010-09-16 04:11:32 +0800312 * @param timeout the timeout value in seconds. Default value (defined by
313 * SIP protocol) is used if {@code timeout} is zero or negative.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800314 * @return a {@link SipAudioCall} object
Hung-ying Tyan5bd37822010-12-20 19:08:24 +0800315 * @throws SipException if calling the SIP service results in an error or
316 * VOIP API is not supported by the device
Scott Main02b1d682010-10-22 11:29:57 -0700317 * @see SipAudioCall.Listener#onError
Hung-ying Tyan5bd37822010-12-20 19:08:24 +0800318 * @see #isVoipSupported
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800319 */
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800320 public SipAudioCall makeAudioCall(SipProfile localProfile,
Hung-ying Tyan9352cf12010-09-16 20:14:18 +0800321 SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800322 throws SipException {
Hung-ying Tyan5bd37822010-12-20 19:08:24 +0800323 if (!isVoipSupported(mContext)) {
324 throw new SipException("VOIP API is not supported");
325 }
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800326 SipAudioCall call = new SipAudioCall(mContext, localProfile);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800327 call.setListener(listener);
Hung-ying Tyan3a4197e2010-09-24 23:27:40 +0800328 SipSession s = createSipSession(localProfile, null);
Hung-ying Tyan3a4197e2010-09-24 23:27:40 +0800329 call.makeCall(peerProfile, s, timeout);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800330 return call;
331 }
332
333 /**
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800334 * Creates a {@link SipAudioCall} to make an audio call. The attempt will be
335 * timed out if the call is not established within {@code timeout} seconds
336 * and
Scott Main02b1d682010-10-22 11:29:57 -0700337 * {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
Hung-ying Tyan9352cf12010-09-16 20:14:18 +0800338 * will be called.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800339 *
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800340 * @param localProfileUri URI of the SIP profile to make the call from
341 * @param peerProfileUri URI of the SIP profile to make the call to
342 * @param listener to listen to the call events from {@link SipAudioCall};
343 * can be null
Hung-ying Tyan08faac32010-09-16 04:11:32 +0800344 * @param timeout the timeout value in seconds. Default value (defined by
345 * SIP protocol) is used if {@code timeout} is zero or negative.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800346 * @return a {@link SipAudioCall} object
Hung-ying Tyan5bd37822010-12-20 19:08:24 +0800347 * @throws SipException if calling the SIP service results in an error or
348 * VOIP API is not supported by the device
Scott Main02b1d682010-10-22 11:29:57 -0700349 * @see SipAudioCall.Listener#onError
Hung-ying Tyan5bd37822010-12-20 19:08:24 +0800350 * @see #isVoipSupported
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800351 */
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800352 public SipAudioCall makeAudioCall(String localProfileUri,
Hung-ying Tyan9352cf12010-09-16 20:14:18 +0800353 String peerProfileUri, SipAudioCall.Listener listener, int timeout)
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800354 throws SipException {
Hung-ying Tyan5bd37822010-12-20 19:08:24 +0800355 if (!isVoipSupported(mContext)) {
356 throw new SipException("VOIP API is not supported");
357 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800358 try {
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800359 return makeAudioCall(
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800360 new SipProfile.Builder(localProfileUri).build(),
Hung-ying Tyan9352cf12010-09-16 20:14:18 +0800361 new SipProfile.Builder(peerProfileUri).build(), listener,
362 timeout);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800363 } catch (ParseException e) {
364 throw new SipException("build SipProfile", e);
365 }
366 }
367
368 /**
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800369 * Creates a {@link SipAudioCall} to take an incoming call. Before the call
370 * is returned, the listener will receive a
Hung-ying Tyan286bb5a2010-09-14 20:43:54 +0800371 * {@link SipAudioCall.Listener#onRinging}
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800372 * callback.
373 *
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800374 * @param incomingCallIntent the incoming call broadcast intent
375 * @param listener to listen to the call events from {@link SipAudioCall};
376 * can be null
377 * @return a {@link SipAudioCall} object
378 * @throws SipException if calling the SIP service results in an error
379 */
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800380 public SipAudioCall takeAudioCall(Intent incomingCallIntent,
Hung-ying Tyan9b449e52010-10-20 18:07:01 +0800381 SipAudioCall.Listener listener) throws SipException {
Hung-ying Tyan8d1b2a12010-11-03 11:50:05 +0800382 if (incomingCallIntent == null) {
383 throw new SipException("Cannot retrieve session with null intent");
384 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800385
386 String callId = getCallId(incomingCallIntent);
387 if (callId == null) {
388 throw new SipException("Call ID missing in incoming call intent");
389 }
390
Chia-chi Yeh95b15c32010-09-02 22:15:26 +0800391 String offerSd = getOfferSessionDescription(incomingCallIntent);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800392 if (offerSd == null) {
393 throw new SipException("Session description missing in incoming "
394 + "call intent");
395 }
396
397 try {
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800398 ISipSession session = mSipService.getPendingSession(callId);
Hung-ying Tyan8d1b2a12010-11-03 11:50:05 +0800399 if (session == null) {
400 throw new SipException("No pending session for the call");
401 }
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800402 SipAudioCall call = new SipAudioCall(
403 mContext, session.getLocalProfile());
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800404 call.attachCall(new SipSession(session), offerSd);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800405 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);
Chia-chi Yeh95b15c32010-09-02 22:15:26 +0800421 String offerSd = getOfferSessionDescription(intent);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800422 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) {
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800432 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800433 }
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 */
Chia-chi Yeh95b15c32010-09-02 22:15:26 +0800443 public static String getOfferSessionDescription(Intent incomingCallIntent) {
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800444 return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800445 }
446
447 /**
448 * Creates an incoming call broadcast intent.
449 *
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800450 * @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 */
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800455 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);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800460 return intent;
461 }
462
463 /**
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800464 * Manually registers the profile to the corresponding SIP provider for
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800465 * 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
Scott Main02b1d682010-10-22 11:29:57 -0700468 * notify the caller with the {@link android.app.PendingIntent} when an incoming call is
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800469 * received.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800470 *
471 * @param localProfile the SIP profile to register with
Hung-ying Tyan286bb5a2010-09-14 20:43:54 +0800472 * @param expiryTime registration expiration time (in seconds)
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800473 * @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 {
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800479 ISipSession session = mSipService.createSession(localProfile,
480 createRelay(listener, localProfile.getUriString()));
Masahiko Endo25ccbb92011-07-28 21:51:43 +0900481 if (session == null) {
482 throw new SipException(
483 "SipService.createSession() returns null");
484 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800485 session.register(expiryTime);
486 } catch (RemoteException e) {
487 throw new SipException("register()", e);
488 }
489 }
490
491 /**
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800492 * Manually unregisters the profile from the corresponding SIP provider for
493 * stop receiving further calls. This may interference with the auto
494 * registration process in the SIP service if the auto-registration option
495 * in the profile is enabled.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800496 *
497 * @param localProfile the SIP profile to register with
498 * @param listener to listen to the registration events
499 * @throws SipException if calling the SIP service results in an error
500 */
501 public void unregister(SipProfile localProfile,
502 SipRegistrationListener listener) throws SipException {
503 try {
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800504 ISipSession session = mSipService.createSession(localProfile,
505 createRelay(listener, localProfile.getUriString()));
Masahiko Endo25ccbb92011-07-28 21:51:43 +0900506 if (session == null) {
507 throw new SipException(
508 "SipService.createSession() returns null");
509 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800510 session.unregister();
511 } catch (RemoteException e) {
512 throw new SipException("unregister()", e);
513 }
514 }
515
516 /**
Hung-ying Tyan08faac32010-09-16 04:11:32 +0800517 * Gets the {@link SipSession} that handles the incoming call. For audio
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800518 * calls, consider to use {@link SipAudioCall} to handle the incoming call.
Hung-ying Tyan286bb5a2010-09-14 20:43:54 +0800519 * See {@link #takeAudioCall}. Note that the method may be called only once
520 * for the same intent. For subsequent calls on the same intent, the method
521 * returns null.
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800522 *
523 * @param incomingCallIntent the incoming call broadcast intent
524 * @return the session object that handles the incoming call
525 */
Hung-ying Tyan08faac32010-09-16 04:11:32 +0800526 public SipSession getSessionFor(Intent incomingCallIntent)
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800527 throws SipException {
528 try {
529 String callId = getCallId(incomingCallIntent);
Hung-ying Tyan08faac32010-09-16 04:11:32 +0800530 ISipSession s = mSipService.getPendingSession(callId);
Masahiko Endo25ccbb92011-07-28 21:51:43 +0900531 return ((s == null) ? null : new SipSession(s));
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800532 } catch (RemoteException e) {
533 throw new SipException("getSessionFor()", e);
534 }
535 }
536
537 private static ISipSessionListener createRelay(
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800538 SipRegistrationListener listener, String uri) {
539 return ((listener == null) ? null : new ListenerRelay(listener, uri));
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800540 }
541
542 /**
Hung-ying Tyan08faac32010-09-16 04:11:32 +0800543 * Creates a {@link SipSession} with the specified profile. Use other
544 * methods, if applicable, instead of interacting with {@link SipSession}
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800545 * directly.
546 *
547 * @param localProfile the SIP profile the session is associated with
548 * @param listener to listen to SIP session events
549 */
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800550 public SipSession createSipSession(SipProfile localProfile,
551 SipSession.Listener listener) throws SipException {
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800552 try {
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800553 ISipSession s = mSipService.createSession(localProfile, null);
Hung-ying Tyan8d1b2a12010-11-03 11:50:05 +0800554 if (s == null) {
555 throw new SipException(
556 "Failed to create SipSession; network unavailable?");
557 }
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800558 return new SipSession(s, listener);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800559 } catch (RemoteException e) {
560 throw new SipException("createSipSession()", e);
561 }
562 }
563
564 /**
565 * Gets the list of profiles hosted by the SIP service. The user information
566 * (username, password and display name) are crossed out.
567 * @hide
568 */
569 public SipProfile[] getListOfProfiles() {
570 try {
571 return mSipService.getListOfProfiles();
572 } catch (RemoteException e) {
Hung-ying Tyan8d1b2a12010-11-03 11:50:05 +0800573 return new SipProfile[0];
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800574 }
575 }
576
577 private static class ListenerRelay extends SipSessionAdapter {
578 private SipRegistrationListener mListener;
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800579 private String mUri;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800580
581 // listener must not be null
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800582 public ListenerRelay(SipRegistrationListener listener, String uri) {
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800583 mListener = listener;
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800584 mUri = uri;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800585 }
586
587 private String getUri(ISipSession session) {
588 try {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800589 return ((session == null)
Hung-ying Tyan9e1d3082010-09-30 15:00:34 +0800590 ? mUri
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800591 : session.getLocalProfile().getUriString());
Hung-ying Tyan9ea96c62010-10-03 20:35:02 +0800592 } catch (Throwable e) {
593 // SipService died? SIP stack died?
594 Log.w(TAG, "getUri(): " + e);
595 return null;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800596 }
597 }
598
599 @Override
600 public void onRegistering(ISipSession session) {
601 mListener.onRegistering(getUri(session));
602 }
603
604 @Override
605 public void onRegistrationDone(ISipSession session, int duration) {
606 long expiryTime = duration;
607 if (duration > 0) expiryTime += System.currentTimeMillis();
608 mListener.onRegistrationDone(getUri(session), expiryTime);
609 }
610
611 @Override
Hung-ying Tyan97963792010-09-17 16:58:51 +0800612 public void onRegistrationFailed(ISipSession session, int errorCode,
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800613 String message) {
Hung-ying Tyan97963792010-09-17 16:58:51 +0800614 mListener.onRegistrationFailed(getUri(session), errorCode, message);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800615 }
616
617 @Override
618 public void onRegistrationTimeout(ISipSession session) {
619 mListener.onRegistrationFailed(getUri(session),
Hung-ying Tyan99bf4e42010-09-14 20:12:59 +0800620 SipErrorCode.TIME_OUT, "registration timed out");
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800621 }
622 }
623}