blob: 5a1b03d3e66d0394150d004079e2f1de88aa53da [file] [log] [blame]
Wink Savilleef36ef62014-06-11 08:39:38 -07001/*
2 * Copyright (c) 2013 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 com.android.ims;
18
19import android.app.PendingIntent;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.os.IBinder;
24import android.os.IBinder.DeathRecipient;
25import android.os.Message;
26import android.os.Process;
27import android.os.RemoteException;
28import android.os.ServiceManager;
29import android.telephony.Rlog;
30
31import com.android.ims.internal.IImsCallSession;
32import com.android.ims.internal.IImsRegistrationListener;
33import com.android.ims.internal.IImsService;
34import com.android.ims.internal.IImsUt;
35import com.android.ims.internal.ImsCallSession;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -050036import com.android.ims.internal.IImsConfig;
37
Wink Savilleef36ef62014-06-11 08:39:38 -070038
39/**
40 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
41 * the operator's IMS network. This class is the starting point for any IMS actions.
42 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
43 * <p>The APIs in this class allows you to:</p>
44 *
45 * @hide
46 */
47public class ImsManager {
48 /**
49 * For accessing the IMS related service.
50 * Internal use only.
51 * @hide
52 */
53 public static final String IMS_SERVICE = "ims";
54
55 /**
56 * The result code to be sent back with the incoming call {@link PendingIntent}.
57 * @see #open(PendingIntent, ImsConnectionStateListener)
58 */
59 public static final int INCOMING_CALL_RESULT_CODE = 101;
60
61 /**
62 * Key to retrieve the call ID from an incoming call intent.
63 * @see #open(PendingIntent, ImsConnectionStateListener)
64 */
65 public static final String EXTRA_CALL_ID = "android:imsCallID";
66
67 /**
68 * Action to broadcast when ImsService is up.
69 * Internal use only.
70 * @hide
71 */
72 public static final String ACTION_IMS_SERVICE_UP =
73 "com.android.ims.IMS_SERVICE_UP";
74
75 /**
76 * Action to broadcast when ImsService is down.
77 * Internal use only.
78 * @hide
79 */
80 public static final String ACTION_IMS_SERVICE_DOWN =
81 "com.android.ims.IMS_SERVICE_DOWN";
82
83 /**
84 * Action for the incoming call intent for the Phone app.
85 * Internal use only.
86 * @hide
87 */
88 public static final String ACTION_IMS_INCOMING_CALL =
89 "com.android.ims.IMS_INCOMING_CALL";
90
91 /**
92 * Part of the ACTION_IMS_INCOMING_CALL intents.
93 * An integer value; service identifier obtained from {@link ImsManager#open}.
94 * Internal use only.
95 * @hide
96 */
97 public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
98
99 /**
100 * Part of the ACTION_IMS_INCOMING_CALL intents.
101 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
102 * The value "true" indicates that the incoming call is for USSD.
103 * Internal use only.
104 * @hide
105 */
106 public static final String EXTRA_USSD = "android:ussd";
107
108
109
110 private static final String TAG = "ImsManager";
111 private static final boolean DBG = true;
112
113 private static ImsManager mImsManager = null;
114 private Context mContext;
115 private IImsService mImsService = null;
116 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
117 // Ut interface for the supplementary service configuration
118 private ImsUt mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500119 // Interface to get/set ims config items
120 private ImsConfig mConfig = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700121
122 /**
123 * Gets a manager instance.
124 *
125 * @param context application context for creating the manager object
126 * @return the manager instance
127 */
128 public static ImsManager getInstance(Context context) {
129 if (mImsManager == null) {
130 mImsManager = new ImsManager(context);
131 }
132
133 return mImsManager;
134 }
135
136 private ImsManager(Context context) {
137 mContext = context;
138 createImsService(true);
139 }
140
141 /**
142 * Opens the IMS service for making calls and/or receiving generic IMS calls.
143 * The caller may make subsquent calls through {@link #makeCall}.
144 * The IMS service will register the device to the operator's network with the credentials
145 * (from ISIM) periodically in order to receive calls from the operator's network.
146 * When the IMS service receives a new call, it will send out an intent with
147 * the provided action string.
148 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
149 *
150 * @param serviceClass a service class specified in {@link ImsServiceClass}
151 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
152 * @param incomingCallPendingIntent When an incoming call is received,
153 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
154 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
155 * as the result code and the intent to fill in the call ID; It cannot be null
156 * @param listener To listen to IMS registration events; It cannot be null
157 * @return identifier (greater than 0) for the specified service
158 * @throws NullPointerException if {@code incomingCallPendingIntent}
159 * or {@code listener} is null
160 * @throws ImsException if calling the IMS service results in an error
161 * @see #getCallId
162 * @see #getServiceId
163 */
164 public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
165 ImsConnectionStateListener listener) throws ImsException {
166 checkAndThrowExceptionIfServiceUnavailable();
167
168 if (incomingCallPendingIntent == null) {
169 throw new NullPointerException("incomingCallPendingIntent can't be null");
170 }
171
172 if (listener == null) {
173 throw new NullPointerException("listener can't be null");
174 }
175
176 int result = 0;
177
178 try {
179 result = mImsService.open(serviceClass, incomingCallPendingIntent,
180 createRegistrationListenerProxy(serviceClass, listener));
181 } catch (RemoteException e) {
182 throw new ImsException("open()", e,
183 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
184 }
185
186 if (result <= 0) {
187 // If the return value is a minus value,
188 // it means that an error occurred in the service.
189 // So, it needs to convert to the reason code specified in ImsReasonInfo.
190 throw new ImsException("open()", (result * (-1)));
191 }
192
193 return result;
194 }
195
196 /**
197 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
198 * All the resources that were allocated to the service are also released.
199 *
200 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
201 * @throws ImsException if calling the IMS service results in an error
202 */
203 public void close(int serviceId) throws ImsException {
204 checkAndThrowExceptionIfServiceUnavailable();
205
206 try {
207 mImsService.close(serviceId);
208 } catch (RemoteException e) {
209 throw new ImsException("close()", e,
210 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
211 } finally {
212 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500213 mConfig = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700214 }
215 }
216
217 /**
218 * Gets the configuration interface to provision / withdraw the supplementary service settings.
219 *
220 * @param serviceId a service id which is obtained from {@link ImsManager#open}
221 * @return the Ut interface instance
222 * @throws ImsException if getting the Ut interface results in an error
223 */
224 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
225 throws ImsException {
226 // FIXME: manage the multiple Ut interfaces based on the service id
227 if (mUt == null) {
228 checkAndThrowExceptionIfServiceUnavailable();
229
230 try {
231 IImsUt iUt = mImsService.getUtInterface(serviceId);
232
233 if (iUt == null) {
234 throw new ImsException("getSupplementaryServiceConfiguration()",
235 ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
236 }
237
238 mUt = new ImsUt(iUt);
239 } catch (RemoteException e) {
240 throw new ImsException("getSupplementaryServiceConfiguration()", e,
241 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
242 }
243 }
244
245 return mUt;
246 }
247
248 /**
249 * Checks if the IMS service has successfully registered to the IMS network
250 * with the specified service & call type.
251 *
252 * @param serviceId a service id which is obtained from {@link ImsManager#open}
253 * @param serviceType a service type that is specified in {@link ImsCallProfile}
254 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
255 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
256 * @param callType a call type that is specified in {@link ImsCallProfile}
257 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
258 * {@link ImsCallProfile#CALL_TYPE_VOICE}
259 * {@link ImsCallProfile#CALL_TYPE_VT}
260 * {@link ImsCallProfile#CALL_TYPE_VS}
261 * @return true if the specified service id is connected to the IMS network;
262 * false otherwise
263 * @throws ImsException if calling the IMS service results in an error
264 */
265 public boolean isConnected(int serviceId, int serviceType, int callType)
266 throws ImsException {
267 checkAndThrowExceptionIfServiceUnavailable();
268
269 try {
270 return mImsService.isConnected(serviceId, serviceType, callType);
271 } catch (RemoteException e) {
272 throw new ImsException("isServiceConnected()", e,
273 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
274 }
275 }
276
277 /**
278 * Checks if the specified IMS service is opend.
279 *
280 * @param serviceId a service id which is obtained from {@link ImsManager#open}
281 * @return true if the specified service id is opened; false otherwise
282 * @throws ImsException if calling the IMS service results in an error
283 */
284 public boolean isOpened(int serviceId) throws ImsException {
285 checkAndThrowExceptionIfServiceUnavailable();
286
287 try {
288 return mImsService.isOpened(serviceId);
289 } catch (RemoteException e) {
290 throw new ImsException("isOpened()", e,
291 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
292 }
293 }
294
295 /**
296 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
297 *
298 * @param serviceId a service id which is obtained from {@link ImsManager#open}
299 * @param serviceType a service type that is specified in {@link ImsCallProfile}
300 * {@link ImsCallProfile#SERVICE_TYPE_NONE}
301 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
302 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
303 * @param callType a call type that is specified in {@link ImsCallProfile}
304 * {@link ImsCallProfile#CALL_TYPE_VOICE}
305 * {@link ImsCallProfile#CALL_TYPE_VT}
306 * {@link ImsCallProfile#CALL_TYPE_VT_TX}
307 * {@link ImsCallProfile#CALL_TYPE_VT_RX}
308 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
309 * {@link ImsCallProfile#CALL_TYPE_VS}
310 * {@link ImsCallProfile#CALL_TYPE_VS_TX}
311 * {@link ImsCallProfile#CALL_TYPE_VS_RX}
312 * @return a {@link ImsCallProfile} object
313 * @throws ImsException if calling the IMS service results in an error
314 */
315 public ImsCallProfile createCallProfile(int serviceId,
316 int serviceType, int callType) throws ImsException {
317 checkAndThrowExceptionIfServiceUnavailable();
318
319 try {
320 return mImsService.createCallProfile(serviceId, serviceType, callType);
321 } catch (RemoteException e) {
322 throw new ImsException("createCallProfile()", e,
323 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
324 }
325 }
326
327 /**
328 * Creates a {@link ImsCall} to make a call.
329 *
330 * @param serviceId a service id which is obtained from {@link ImsManager#open}
331 * @param profile a call profile to make the call
332 * (it contains service type, call type, media information, etc.)
333 * @param participants participants to invite the conference call
334 * @param listener listen to the call events from {@link ImsCall}
335 * @return a {@link ImsCall} object
336 * @throws ImsException if calling the IMS service results in an error
337 */
338 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
339 ImsCall.Listener listener) throws ImsException {
340 if (DBG) {
341 log("makeCall :: serviceId=" + serviceId
342 + ", profile=" + profile + ", callees=" + callees);
343 }
344
345 checkAndThrowExceptionIfServiceUnavailable();
346
347 ImsCall call = new ImsCall(mContext, profile);
348
349 call.setListener(listener);
350 ImsCallSession session = createCallSession(serviceId, profile);
351
352 if ((callees != null) && (callees.length == 1)) {
353 call.start(session, callees[0]);
354 } else {
355 call.start(session, callees);
356 }
357
358 return call;
359 }
360
361 /**
362 * Creates a {@link ImsCall} to take an incoming call.
363 *
364 * @param serviceId a service id which is obtained from {@link ImsManager#open}
365 * @param incomingCallIntent the incoming call broadcast intent
366 * @param listener to listen to the call events from {@link ImsCall}
367 * @return a {@link ImsCall} object
368 * @throws ImsException if calling the IMS service results in an error
369 */
370 public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
371 ImsCall.Listener listener) throws ImsException {
372 if (DBG) {
373 log("takeCall :: serviceId=" + serviceId
374 + ", incomingCall=" + incomingCallIntent);
375 }
376
377 checkAndThrowExceptionIfServiceUnavailable();
378
379 if (incomingCallIntent == null) {
380 throw new ImsException("Can't retrieve session with null intent",
381 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
382 }
383
384 int incomingServiceId = getServiceId(incomingCallIntent);
385
386 if (serviceId != incomingServiceId) {
387 throw new ImsException("Service id is mismatched in the incoming call intent",
388 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
389 }
390
391 String callId = getCallId(incomingCallIntent);
392
393 if (callId == null) {
394 throw new ImsException("Call ID missing in the incoming call intent",
395 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
396 }
397
398 try {
399 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
400
401 if (session == null) {
402 throw new ImsException("No pending session for the call",
403 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
404 }
405
406 ImsCall call = new ImsCall(mContext, session.getCallProfile());
407
408 call.attachSession(new ImsCallSession(session));
409 call.setListener(listener);
410
411 return call;
412 } catch (Throwable t) {
413 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
414 }
415 }
416
417 /**
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500418 * Gets the config interface to get/set service/capability parameters.
419 *
420 * @return the ImsConfig instance.
421 * @throws ImsException if getting the setting interface results in an error.
422 */
423 public ImsConfig getConfigInterface() throws ImsException {
424
425 if (mConfig == null) {
426 checkAndThrowExceptionIfServiceUnavailable();
427
428 try {
429 IImsConfig config = mImsService.getConfigInterface();
430 if (config == null) {
431 throw new ImsException("getConfigInterface()",
432 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
433 }
434 mConfig = new ImsConfig(config);
435 } catch (RemoteException e) {
436 throw new ImsException("getConfigInterface()", e,
437 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
438 }
439 }
440 if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
441 return mConfig;
442 }
443
444 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700445 * Gets the call ID from the specified incoming call broadcast intent.
446 *
447 * @param incomingCallIntent the incoming call broadcast intent
448 * @return the call ID or null if the intent does not contain it
449 */
450 private static String getCallId(Intent incomingCallIntent) {
451 if (incomingCallIntent == null) {
452 return null;
453 }
454
455 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
456 }
457
458 /**
459 * Gets the service type from the specified incoming call broadcast intent.
460 *
461 * @param incomingCallIntent the incoming call broadcast intent
462 * @return the service identifier or -1 if the intent does not contain it
463 */
464 private static int getServiceId(Intent incomingCallIntent) {
465 if (incomingCallIntent == null) {
466 return (-1);
467 }
468
469 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
470 }
471
472 /**
473 * Binds the IMS service only if the service is not created.
474 */
475 private void checkAndThrowExceptionIfServiceUnavailable()
476 throws ImsException {
477 if (mImsService == null) {
478 createImsService(true);
479
480 if (mImsService == null) {
481 throw new ImsException("Service is unavailable",
482 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
483 }
484 }
485 }
486
487 /**
488 * Binds the IMS service to make/receive the call.
489 */
490 private void createImsService(boolean checkService) {
491 if (checkService) {
492 IBinder binder = ServiceManager.checkService(IMS_SERVICE);
493
494 if (binder == null) {
495 return;
496 }
497 }
498
499 IBinder b = ServiceManager.getService(IMS_SERVICE);
500
501 if (b != null) {
502 try {
503 b.linkToDeath(mDeathRecipient, 0);
504 } catch (RemoteException e) {
505 }
506 }
507
508 mImsService = IImsService.Stub.asInterface(b);
509 }
510
511 /**
512 * Creates a {@link ImsCallSession} with the specified call profile.
513 * Use other methods, if applicable, instead of interacting with
514 * {@link ImsCallSession} directly.
515 *
516 * @param serviceId a service id which is obtained from {@link ImsManager#open}
517 * @param profile a call profile to make the call
518 */
519 private ImsCallSession createCallSession(int serviceId,
520 ImsCallProfile profile) throws ImsException {
521 try {
522 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
523 } catch (RemoteException e) {
524 return null;
525 }
526 }
527
528 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
529 ImsConnectionStateListener listener) {
530 ImsRegistrationListenerProxy proxy =
531 new ImsRegistrationListenerProxy(serviceClass, listener);
532 return proxy;
533 }
534
535 private void log(String s) {
536 Rlog.d(TAG, s);
537 }
538
539 private void loge(String s) {
540 Rlog.e(TAG, s);
541 }
542
543 private void loge(String s, Throwable t) {
544 Rlog.e(TAG, s, t);
545 }
546
547 /**
548 * Death recipient class for monitoring IMS service.
549 */
550 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
551 @Override
552 public void binderDied() {
553 mImsService = null;
554 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500555 mConfig = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700556
557 if (mContext != null) {
558 mContext.sendBroadcast(new Intent(ACTION_IMS_SERVICE_DOWN));
559 }
560 }
561 }
562
563 /**
564 * Adapter class for {@link IImsRegistrationListener}.
565 */
566 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
567 private int mServiceClass;
568 private ImsConnectionStateListener mListener;
569
570 public ImsRegistrationListenerProxy(int serviceClass,
571 ImsConnectionStateListener listener) {
572 mServiceClass = serviceClass;
573 mListener = listener;
574 }
575
576 public boolean isSameProxy(int serviceClass) {
577 return (mServiceClass == serviceClass);
578 }
579
580 @Override
581 public void registrationConnected() {
582 if (DBG) {
583 log("registrationConnected ::");
584 }
585
586 if (mListener != null) {
587 mListener.onImsConnected();
588 }
589 }
590
591 @Override
592 public void registrationDisconnected() {
593 if (DBG) {
594 log("registrationDisconnected ::");
595 }
596
597 if (mListener != null) {
598 mListener.onImsDisconnected();
599 }
600 }
601
602 @Override
603 public void registrationResumed() {
604 if (DBG) {
605 log("registrationResumed ::");
606 }
607
608 if (mListener != null) {
609 mListener.onImsResumed();
610 }
611 }
612
613 @Override
614 public void registrationSuspended() {
615 if (DBG) {
616 log("registrationSuspended ::");
617 }
618
619 if (mListener != null) {
620 mListener.onImsSuspended();
621 }
622 }
623
624 @Override
625 public void registrationServiceCapabilityChanged(int serviceClass, int event) {
626 log("registrationServiceCapabilityChanged :: serviceClass=" +
627 serviceClass + ", event=" + event);
628
629 if (mListener != null) {
630 mListener.onImsConnected();
631 }
632 }
633 }
634}