blob: 0cdf66dd9d024da364cfef4ed4319be8da7408d5 [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;
Wink Savilleef36ef62014-06-11 08:39:38 -070022import android.os.IBinder;
Wink Savilleef36ef62014-06-11 08:39:38 -070023import android.os.Message;
Wink Savilleef36ef62014-06-11 08:39:38 -070024import android.os.RemoteException;
25import android.os.ServiceManager;
Etan Cohenaf55a402014-09-04 22:34:41 -070026import android.os.SystemProperties;
Etan Cohen82f78122014-12-15 10:10:14 -080027import android.provider.Settings;
28import android.telecom.TelecomManager;
Wink Savilleef36ef62014-06-11 08:39:38 -070029import android.telephony.Rlog;
Etan Cohen82f78122014-12-15 10:10:14 -080030import android.telephony.SubscriptionManager;
Etan Cohencfc784d2014-08-07 18:40:31 -070031import android.telephony.TelephonyManager;
Wink Savilleef36ef62014-06-11 08:39:38 -070032
33import com.android.ims.internal.IImsCallSession;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -070034import com.android.ims.internal.IImsEcbm;
35import com.android.ims.internal.IImsEcbmListener;
Wink Savilleef36ef62014-06-11 08:39:38 -070036import com.android.ims.internal.IImsRegistrationListener;
37import com.android.ims.internal.IImsService;
38import com.android.ims.internal.IImsUt;
39import com.android.ims.internal.ImsCallSession;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -050040import com.android.ims.internal.IImsConfig;
41
Etan Cohend7727462014-07-12 14:54:10 -070042import java.util.HashMap;
43
Wink Savilleef36ef62014-06-11 08:39:38 -070044/**
45 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
46 * the operator's IMS network. This class is the starting point for any IMS actions.
47 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
48 * <p>The APIs in this class allows you to:</p>
49 *
50 * @hide
51 */
52public class ImsManager {
Etan Cohen19604c02014-08-11 14:32:57 -070053
Etan Cohenaf55a402014-09-04 22:34:41 -070054 /*
55 * Debug flag to override configuration flag
56 */
Etan Cohenb651fa52014-10-22 10:51:29 -070057 public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
58 public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenea2b5832014-10-23 18:50:35 -070059 public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
60 public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenaf55a402014-09-04 22:34:41 -070061
Wink Savilleef36ef62014-06-11 08:39:38 -070062 /**
63 * For accessing the IMS related service.
64 * Internal use only.
65 * @hide
66 */
Etan Cohend7727462014-07-12 14:54:10 -070067 private static final String IMS_SERVICE = "ims";
Wink Savilleef36ef62014-06-11 08:39:38 -070068
69 /**
70 * The result code to be sent back with the incoming call {@link PendingIntent}.
71 * @see #open(PendingIntent, ImsConnectionStateListener)
72 */
73 public static final int INCOMING_CALL_RESULT_CODE = 101;
74
75 /**
76 * Key to retrieve the call ID from an incoming call intent.
77 * @see #open(PendingIntent, ImsConnectionStateListener)
78 */
79 public static final String EXTRA_CALL_ID = "android:imsCallID";
80
81 /**
82 * Action to broadcast when ImsService is up.
83 * Internal use only.
84 * @hide
85 */
86 public static final String ACTION_IMS_SERVICE_UP =
87 "com.android.ims.IMS_SERVICE_UP";
88
89 /**
90 * Action to broadcast when ImsService is down.
91 * Internal use only.
92 * @hide
93 */
94 public static final String ACTION_IMS_SERVICE_DOWN =
95 "com.android.ims.IMS_SERVICE_DOWN";
96
97 /**
Etan Cohend7727462014-07-12 14:54:10 -070098 * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
Etan Cohenabbd7882014-09-26 22:35:35 -070099 * A long value; the phone ID corresponding to the IMS service coming up or down.
Etan Cohend7727462014-07-12 14:54:10 -0700100 * Internal use only.
101 * @hide
102 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700103 public static final String EXTRA_PHONE_ID = "android:phone_id";
Etan Cohend7727462014-07-12 14:54:10 -0700104
105 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700106 * Action for the incoming call intent for the Phone app.
107 * Internal use only.
108 * @hide
109 */
110 public static final String ACTION_IMS_INCOMING_CALL =
111 "com.android.ims.IMS_INCOMING_CALL";
112
113 /**
114 * Part of the ACTION_IMS_INCOMING_CALL intents.
115 * An integer value; service identifier obtained from {@link ImsManager#open}.
116 * Internal use only.
117 * @hide
118 */
119 public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
120
121 /**
122 * Part of the ACTION_IMS_INCOMING_CALL intents.
123 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
124 * The value "true" indicates that the incoming call is for USSD.
125 * Internal use only.
126 * @hide
127 */
128 public static final String EXTRA_USSD = "android:ussd";
129
Wink Savilleef36ef62014-06-11 08:39:38 -0700130 private static final String TAG = "ImsManager";
131 private static final boolean DBG = true;
132
Wink Saville1e5a38a2014-10-23 10:24:46 -0700133 private static HashMap<Integer, ImsManager> sImsManagerInstances =
134 new HashMap<Integer, ImsManager>();
Etan Cohend7727462014-07-12 14:54:10 -0700135
Wink Savilleef36ef62014-06-11 08:39:38 -0700136 private Context mContext;
Etan Cohenabbd7882014-09-26 22:35:35 -0700137 private int mPhoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700138 private IImsService mImsService = null;
139 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
140 // Ut interface for the supplementary service configuration
141 private ImsUt mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500142 // Interface to get/set ims config items
143 private ImsConfig mConfig = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700144
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700145 // ECBM interface
146 private ImsEcbm mEcbm = null;
147
Wink Savilleef36ef62014-06-11 08:39:38 -0700148 /**
149 * Gets a manager instance.
150 *
151 * @param context application context for creating the manager object
Etan Cohenabbd7882014-09-26 22:35:35 -0700152 * @param phoneId the phone ID for the IMS Service
153 * @return the manager instance corresponding to the phoneId
Wink Savilleef36ef62014-06-11 08:39:38 -0700154 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700155 public static ImsManager getInstance(Context context, int phoneId) {
Etan Cohend7727462014-07-12 14:54:10 -0700156 synchronized (sImsManagerInstances) {
Etan Cohenabbd7882014-09-26 22:35:35 -0700157 if (sImsManagerInstances.containsKey(phoneId))
158 return sImsManagerInstances.get(phoneId);
Wink Savilleef36ef62014-06-11 08:39:38 -0700159
Etan Cohenabbd7882014-09-26 22:35:35 -0700160 ImsManager mgr = new ImsManager(context, phoneId);
161 sImsManagerInstances.put(phoneId, mgr);
Etan Cohend7727462014-07-12 14:54:10 -0700162
163 return mgr;
164 }
Wink Savilleef36ef62014-06-11 08:39:38 -0700165 }
166
Etan Cohen45b5f312014-08-19 15:55:08 -0700167 /**
168 * Returns the user configuration of Enhanced 4G LTE Mode setting
169 */
170 public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
Libin.Tang@motorola.com8a6bf4b2014-10-10 15:02:41 -0500171 int enabled = android.provider.Settings.Global.getInt(
172 context.getContentResolver(),
Etan Cohenb651fa52014-10-22 10:51:29 -0700173 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
Libin.Tang@motorola.com8a6bf4b2014-10-10 15:02:41 -0500174 return (enabled == 1)? true:false;
Etan Cohen45b5f312014-08-19 15:55:08 -0700175 }
176
177 /**
Etan Cohen82f78122014-12-15 10:10:14 -0800178 * Change persistent Enhanced 4G LTE Mode setting
179 */
180 public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
181 int value = enabled ? 1 : 0;
182 android.provider.Settings.Global.putInt(
183 context.getContentResolver(),
184 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
185
186 if (isNonTtyOrTtyOnVolteEnabled(context)) {
187 ImsManager imsManager = ImsManager.getInstance(context,
188 SubscriptionManager.getDefaultVoicePhoneId());
189 if (imsManager != null) {
190 try {
191 imsManager.setAdvanced4GMode(enabled);
192 } catch (ImsException ie) {
193 // do nothing
194 }
195 }
196 }
197 }
198
199 /**
200 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
201 * supported.
202 */
203 public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
204 if (context.getResources().getBoolean(
205 com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
206 return true;
207 }
208
209 return Settings.Secure.getInt(context.getContentResolver(),
210 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
211 == TelecomManager.TTY_MODE_OFF;
212 }
213
214 /**
Etan Cohenea2b5832014-10-23 18:50:35 -0700215 * Returns a platform configuration for VoLTE which may override the user setting.
Etan Cohen45b5f312014-08-19 15:55:08 -0700216 */
Etan Cohenea2b5832014-10-23 18:50:35 -0700217 public static boolean isVolteEnabledByPlatform(Context context) {
Etan Cohenb651fa52014-10-22 10:51:29 -0700218 if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
219 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
Etan Cohenaf55a402014-09-04 22:34:41 -0700220 return true;
221 }
222
Etan Cohend40d4a92014-11-24 11:18:33 -0800223 boolean disabledByGlobalSetting = android.provider.Settings.Global.getInt(
224 context.getContentResolver(),
225 android.provider.Settings.Global.VOLTE_FEATURE_DISABLED, 0) == 1;
226
Etan Cohenb5388a32014-11-26 11:57:47 -0800227 return context.getResources().getBoolean(
228 com.android.internal.R.bool.config_device_volte_available) && context.getResources()
229 .getBoolean(com.android.internal.R.bool.config_carrier_volte_available)
230 && !disabledByGlobalSetting;
231 }
232
233 /*
234 * Indicates whether VoLTE is provisioned on device
235 */
236 public static boolean isVolteProvisionedOnDevice(Context context) {
237 boolean isProvisioned = true;
238 if (context.getResources().getBoolean(
239 com.android.internal.R.bool.config_carrier_volte_provisioned)) {
240 isProvisioned = false; // disable on any error
241 ImsManager mgr = ImsManager.getInstance(context,
242 SubscriptionManager.getDefaultVoiceSubId());
243 if (mgr != null) {
244 try {
245 ImsConfig config = mgr.getConfigInterface();
246 if (config != null) {
247 isProvisioned = config.getVolteProvisioned();
248 }
249 } catch (ImsException ie) {
250 // do nothing
251 }
252 }
253 }
254
255 return isProvisioned;
Etan Cohenb651fa52014-10-22 10:51:29 -0700256 }
257
Etan Cohenea2b5832014-10-23 18:50:35 -0700258 /**
259 * Returns a platform configuration for VT which may override the user setting.
260 *
261 * Note: VT presumes that VoLTE is enabled (these are configuration settings
262 * which must be done correctly).
263 */
Etan Cohenb651fa52014-10-22 10:51:29 -0700264 public static boolean isVtEnabledByPlatform(Context context) {
Etan Cohenea2b5832014-10-23 18:50:35 -0700265 if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
266 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
267 return true;
268 }
269
Etan Cohenb651fa52014-10-22 10:51:29 -0700270 return
271 context.getResources().getBoolean(
272 com.android.internal.R.bool.config_device_vt_available) &&
273 context.getResources().getBoolean(
274 com.android.internal.R.bool.config_carrier_vt_available);
Etan Cohen45b5f312014-08-19 15:55:08 -0700275 }
276
Etan Cohenabbd7882014-09-26 22:35:35 -0700277 private ImsManager(Context context, int phoneId) {
Wink Savilleef36ef62014-06-11 08:39:38 -0700278 mContext = context;
Etan Cohenabbd7882014-09-26 22:35:35 -0700279 mPhoneId = phoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700280 createImsService(true);
281 }
282
283 /**
284 * Opens the IMS service for making calls and/or receiving generic IMS calls.
285 * The caller may make subsquent calls through {@link #makeCall}.
286 * The IMS service will register the device to the operator's network with the credentials
287 * (from ISIM) periodically in order to receive calls from the operator's network.
288 * When the IMS service receives a new call, it will send out an intent with
289 * the provided action string.
290 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
291 *
292 * @param serviceClass a service class specified in {@link ImsServiceClass}
293 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
294 * @param incomingCallPendingIntent When an incoming call is received,
295 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
296 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
297 * as the result code and the intent to fill in the call ID; It cannot be null
298 * @param listener To listen to IMS registration events; It cannot be null
299 * @return identifier (greater than 0) for the specified service
300 * @throws NullPointerException if {@code incomingCallPendingIntent}
301 * or {@code listener} is null
302 * @throws ImsException if calling the IMS service results in an error
303 * @see #getCallId
304 * @see #getServiceId
305 */
306 public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
307 ImsConnectionStateListener listener) throws ImsException {
308 checkAndThrowExceptionIfServiceUnavailable();
309
310 if (incomingCallPendingIntent == null) {
311 throw new NullPointerException("incomingCallPendingIntent can't be null");
312 }
313
314 if (listener == null) {
315 throw new NullPointerException("listener can't be null");
316 }
317
318 int result = 0;
319
320 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700321 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
Wink Savilleef36ef62014-06-11 08:39:38 -0700322 createRegistrationListenerProxy(serviceClass, listener));
323 } catch (RemoteException e) {
324 throw new ImsException("open()", e,
325 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
326 }
327
328 if (result <= 0) {
329 // If the return value is a minus value,
330 // it means that an error occurred in the service.
331 // So, it needs to convert to the reason code specified in ImsReasonInfo.
332 throw new ImsException("open()", (result * (-1)));
333 }
334
335 return result;
336 }
337
338 /**
339 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
340 * All the resources that were allocated to the service are also released.
341 *
342 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
343 * @throws ImsException if calling the IMS service results in an error
344 */
345 public void close(int serviceId) throws ImsException {
346 checkAndThrowExceptionIfServiceUnavailable();
347
348 try {
349 mImsService.close(serviceId);
350 } catch (RemoteException e) {
351 throw new ImsException("close()", e,
352 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
353 } finally {
354 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500355 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700356 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700357 }
358 }
359
360 /**
361 * Gets the configuration interface to provision / withdraw the supplementary service settings.
362 *
363 * @param serviceId a service id which is obtained from {@link ImsManager#open}
364 * @return the Ut interface instance
365 * @throws ImsException if getting the Ut interface results in an error
366 */
367 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
368 throws ImsException {
369 // FIXME: manage the multiple Ut interfaces based on the service id
370 if (mUt == null) {
371 checkAndThrowExceptionIfServiceUnavailable();
372
373 try {
374 IImsUt iUt = mImsService.getUtInterface(serviceId);
375
376 if (iUt == null) {
377 throw new ImsException("getSupplementaryServiceConfiguration()",
378 ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
379 }
380
381 mUt = new ImsUt(iUt);
382 } catch (RemoteException e) {
383 throw new ImsException("getSupplementaryServiceConfiguration()", e,
384 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
385 }
386 }
387
388 return mUt;
389 }
390
391 /**
392 * Checks if the IMS service has successfully registered to the IMS network
393 * with the specified service & call type.
394 *
395 * @param serviceId a service id which is obtained from {@link ImsManager#open}
396 * @param serviceType a service type that is specified in {@link ImsCallProfile}
397 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
398 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
399 * @param callType a call type that is specified in {@link ImsCallProfile}
400 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
401 * {@link ImsCallProfile#CALL_TYPE_VOICE}
402 * {@link ImsCallProfile#CALL_TYPE_VT}
403 * {@link ImsCallProfile#CALL_TYPE_VS}
404 * @return true if the specified service id is connected to the IMS network;
405 * false otherwise
406 * @throws ImsException if calling the IMS service results in an error
407 */
408 public boolean isConnected(int serviceId, int serviceType, int callType)
409 throws ImsException {
410 checkAndThrowExceptionIfServiceUnavailable();
411
412 try {
413 return mImsService.isConnected(serviceId, serviceType, callType);
414 } catch (RemoteException e) {
415 throw new ImsException("isServiceConnected()", e,
416 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
417 }
418 }
419
420 /**
421 * Checks if the specified IMS service is opend.
422 *
423 * @param serviceId a service id which is obtained from {@link ImsManager#open}
424 * @return true if the specified service id is opened; false otherwise
425 * @throws ImsException if calling the IMS service results in an error
426 */
427 public boolean isOpened(int serviceId) throws ImsException {
428 checkAndThrowExceptionIfServiceUnavailable();
429
430 try {
431 return mImsService.isOpened(serviceId);
432 } catch (RemoteException e) {
433 throw new ImsException("isOpened()", e,
434 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
435 }
436 }
437
438 /**
439 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
440 *
441 * @param serviceId a service id which is obtained from {@link ImsManager#open}
442 * @param serviceType a service type that is specified in {@link ImsCallProfile}
443 * {@link ImsCallProfile#SERVICE_TYPE_NONE}
444 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
445 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
446 * @param callType a call type that is specified in {@link ImsCallProfile}
447 * {@link ImsCallProfile#CALL_TYPE_VOICE}
448 * {@link ImsCallProfile#CALL_TYPE_VT}
449 * {@link ImsCallProfile#CALL_TYPE_VT_TX}
450 * {@link ImsCallProfile#CALL_TYPE_VT_RX}
451 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
452 * {@link ImsCallProfile#CALL_TYPE_VS}
453 * {@link ImsCallProfile#CALL_TYPE_VS_TX}
454 * {@link ImsCallProfile#CALL_TYPE_VS_RX}
455 * @return a {@link ImsCallProfile} object
456 * @throws ImsException if calling the IMS service results in an error
457 */
458 public ImsCallProfile createCallProfile(int serviceId,
459 int serviceType, int callType) throws ImsException {
460 checkAndThrowExceptionIfServiceUnavailable();
461
462 try {
463 return mImsService.createCallProfile(serviceId, serviceType, callType);
464 } catch (RemoteException e) {
465 throw new ImsException("createCallProfile()", e,
466 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
467 }
468 }
469
470 /**
471 * Creates a {@link ImsCall} to make a call.
472 *
473 * @param serviceId a service id which is obtained from {@link ImsManager#open}
474 * @param profile a call profile to make the call
475 * (it contains service type, call type, media information, etc.)
476 * @param participants participants to invite the conference call
477 * @param listener listen to the call events from {@link ImsCall}
478 * @return a {@link ImsCall} object
479 * @throws ImsException if calling the IMS service results in an error
480 */
481 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
482 ImsCall.Listener listener) throws ImsException {
483 if (DBG) {
484 log("makeCall :: serviceId=" + serviceId
485 + ", profile=" + profile + ", callees=" + callees);
486 }
487
488 checkAndThrowExceptionIfServiceUnavailable();
489
490 ImsCall call = new ImsCall(mContext, profile);
491
492 call.setListener(listener);
493 ImsCallSession session = createCallSession(serviceId, profile);
494
495 if ((callees != null) && (callees.length == 1)) {
496 call.start(session, callees[0]);
497 } else {
498 call.start(session, callees);
499 }
500
501 return call;
502 }
503
504 /**
505 * Creates a {@link ImsCall} to take an incoming call.
506 *
507 * @param serviceId a service id which is obtained from {@link ImsManager#open}
508 * @param incomingCallIntent the incoming call broadcast intent
509 * @param listener to listen to the call events from {@link ImsCall}
510 * @return a {@link ImsCall} object
511 * @throws ImsException if calling the IMS service results in an error
512 */
513 public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
514 ImsCall.Listener listener) throws ImsException {
515 if (DBG) {
516 log("takeCall :: serviceId=" + serviceId
517 + ", incomingCall=" + incomingCallIntent);
518 }
519
520 checkAndThrowExceptionIfServiceUnavailable();
521
522 if (incomingCallIntent == null) {
523 throw new ImsException("Can't retrieve session with null intent",
524 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
525 }
526
527 int incomingServiceId = getServiceId(incomingCallIntent);
528
529 if (serviceId != incomingServiceId) {
530 throw new ImsException("Service id is mismatched in the incoming call intent",
531 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
532 }
533
534 String callId = getCallId(incomingCallIntent);
535
536 if (callId == null) {
537 throw new ImsException("Call ID missing in the incoming call intent",
538 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
539 }
540
541 try {
542 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
543
544 if (session == null) {
545 throw new ImsException("No pending session for the call",
546 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
547 }
548
549 ImsCall call = new ImsCall(mContext, session.getCallProfile());
550
551 call.attachSession(new ImsCallSession(session));
552 call.setListener(listener);
553
554 return call;
555 } catch (Throwable t) {
556 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
557 }
558 }
559
560 /**
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500561 * Gets the config interface to get/set service/capability parameters.
562 *
563 * @return the ImsConfig instance.
564 * @throws ImsException if getting the setting interface results in an error.
565 */
566 public ImsConfig getConfigInterface() throws ImsException {
567
568 if (mConfig == null) {
569 checkAndThrowExceptionIfServiceUnavailable();
570
571 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700572 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500573 if (config == null) {
574 throw new ImsException("getConfigInterface()",
575 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
576 }
Libin.Tang@motorola.com54953c72014-08-07 15:02:08 -0500577 mConfig = new ImsConfig(config, mContext);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500578 } catch (RemoteException e) {
579 throw new ImsException("getConfigInterface()", e,
580 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
581 }
582 }
583 if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
584 return mConfig;
585 }
586
Etan Cohen82f78122014-12-15 10:10:14 -0800587 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530588 throws ImsException {
589
Etan Cohen82f78122014-12-15 10:10:14 -0800590 checkAndThrowExceptionIfServiceUnavailable();
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530591
Etan Cohen82f78122014-12-15 10:10:14 -0800592 try {
593 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
594 } catch (RemoteException e) {
595 throw new ImsException("setTTYMode()", e,
596 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
597 }
598
599 if (!context.getResources().getBoolean(
600 com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
601 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
602 isEnhanced4gLteModeSettingEnabledByUser(context));
603 }
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530604 }
605
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500606 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700607 * Gets the call ID from the specified incoming call broadcast intent.
608 *
609 * @param incomingCallIntent the incoming call broadcast intent
610 * @return the call ID or null if the intent does not contain it
611 */
612 private static String getCallId(Intent incomingCallIntent) {
613 if (incomingCallIntent == null) {
614 return null;
615 }
616
617 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
618 }
619
620 /**
621 * Gets the service type from the specified incoming call broadcast intent.
622 *
623 * @param incomingCallIntent the incoming call broadcast intent
624 * @return the service identifier or -1 if the intent does not contain it
625 */
626 private static int getServiceId(Intent incomingCallIntent) {
627 if (incomingCallIntent == null) {
628 return (-1);
629 }
630
631 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
632 }
633
634 /**
635 * Binds the IMS service only if the service is not created.
636 */
637 private void checkAndThrowExceptionIfServiceUnavailable()
638 throws ImsException {
639 if (mImsService == null) {
640 createImsService(true);
641
642 if (mImsService == null) {
643 throw new ImsException("Service is unavailable",
644 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
645 }
646 }
647 }
648
Etan Cohenabbd7882014-09-26 22:35:35 -0700649 private static String getImsServiceName(int phoneId) {
650 // TODO: MSIM implementation needs to decide on service name as a function of phoneId
Etan Cohend7727462014-07-12 14:54:10 -0700651 return IMS_SERVICE;
652 }
653
Wink Savilleef36ef62014-06-11 08:39:38 -0700654 /**
655 * Binds the IMS service to make/receive the call.
656 */
657 private void createImsService(boolean checkService) {
658 if (checkService) {
Etan Cohenabbd7882014-09-26 22:35:35 -0700659 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -0700660
661 if (binder == null) {
662 return;
663 }
664 }
665
Etan Cohenabbd7882014-09-26 22:35:35 -0700666 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -0700667
668 if (b != null) {
669 try {
670 b.linkToDeath(mDeathRecipient, 0);
671 } catch (RemoteException e) {
672 }
673 }
674
675 mImsService = IImsService.Stub.asInterface(b);
676 }
677
678 /**
679 * Creates a {@link ImsCallSession} with the specified call profile.
680 * Use other methods, if applicable, instead of interacting with
681 * {@link ImsCallSession} directly.
682 *
683 * @param serviceId a service id which is obtained from {@link ImsManager#open}
684 * @param profile a call profile to make the call
685 */
686 private ImsCallSession createCallSession(int serviceId,
687 ImsCallProfile profile) throws ImsException {
688 try {
689 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
690 } catch (RemoteException e) {
691 return null;
692 }
693 }
694
695 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
696 ImsConnectionStateListener listener) {
697 ImsRegistrationListenerProxy proxy =
698 new ImsRegistrationListenerProxy(serviceClass, listener);
699 return proxy;
700 }
701
702 private void log(String s) {
703 Rlog.d(TAG, s);
704 }
705
706 private void loge(String s) {
707 Rlog.e(TAG, s);
708 }
709
710 private void loge(String s, Throwable t) {
711 Rlog.e(TAG, s, t);
712 }
713
714 /**
ram7da5a112014-07-16 20:59:27 +0530715 * Used for turning on IMS.if its off already
716 */
Etan Cohen82f78122014-12-15 10:10:14 -0800717 private void turnOnIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -0700718 checkAndThrowExceptionIfServiceUnavailable();
719
ram7da5a112014-07-16 20:59:27 +0530720 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700721 mImsService.turnOnIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +0530722 } catch (RemoteException e) {
723 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
724 }
725 }
726
Etan Cohen82f78122014-12-15 10:10:14 -0800727 private void setAdvanced4GMode(boolean turnOn) throws ImsException {
Etan Cohencfc784d2014-08-07 18:40:31 -0700728 checkAndThrowExceptionIfServiceUnavailable();
729
730 ImsConfig config = getConfigInterface();
731 if (config != null) {
732 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
733 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
Etan Cohenb651fa52014-10-22 10:51:29 -0700734 if (isVtEnabledByPlatform(mContext)) {
735 // TODO: once VT is available on platform replace the '1' with the current
736 // user configuration of VT.
737 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
738 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
739 }
Etan Cohencfc784d2014-08-07 18:40:31 -0700740 }
741
742 if (turnOn) {
743 turnOnIms();
744 } else if (mContext.getResources().getBoolean(
745 com.android.internal.R.bool.imsServiceAllowTurnOff)) {
746 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms");
747 turnOffIms();
748 }
749 }
750
ram7da5a112014-07-16 20:59:27 +0530751 /**
752 * Used for turning off IMS completely in order to make the device CSFB'ed.
753 * Once turned off, all calls will be over CS.
754 */
Etan Cohen82f78122014-12-15 10:10:14 -0800755 private void turnOffIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -0700756 checkAndThrowExceptionIfServiceUnavailable();
757
ram7da5a112014-07-16 20:59:27 +0530758 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700759 mImsService.turnOffIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +0530760 } catch (RemoteException e) {
761 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
762 }
763 }
764
765 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700766 * Death recipient class for monitoring IMS service.
767 */
768 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
769 @Override
770 public void binderDied() {
771 mImsService = null;
772 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500773 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700774 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700775
776 if (mContext != null) {
Etan Cohend7727462014-07-12 14:54:10 -0700777 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
Etan Cohenabbd7882014-09-26 22:35:35 -0700778 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
Etan Cohend7727462014-07-12 14:54:10 -0700779 mContext.sendBroadcast(new Intent(intent));
Wink Savilleef36ef62014-06-11 08:39:38 -0700780 }
781 }
782 }
783
784 /**
785 * Adapter class for {@link IImsRegistrationListener}.
786 */
787 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
788 private int mServiceClass;
789 private ImsConnectionStateListener mListener;
790
791 public ImsRegistrationListenerProxy(int serviceClass,
792 ImsConnectionStateListener listener) {
793 mServiceClass = serviceClass;
794 mListener = listener;
795 }
796
797 public boolean isSameProxy(int serviceClass) {
798 return (mServiceClass == serviceClass);
799 }
800
801 @Override
802 public void registrationConnected() {
803 if (DBG) {
804 log("registrationConnected ::");
805 }
806
807 if (mListener != null) {
808 mListener.onImsConnected();
809 }
810 }
811
812 @Override
813 public void registrationDisconnected() {
814 if (DBG) {
815 log("registrationDisconnected ::");
816 }
817
818 if (mListener != null) {
819 mListener.onImsDisconnected();
820 }
821 }
822
823 @Override
824 public void registrationResumed() {
825 if (DBG) {
826 log("registrationResumed ::");
827 }
828
829 if (mListener != null) {
830 mListener.onImsResumed();
831 }
832 }
833
834 @Override
835 public void registrationSuspended() {
836 if (DBG) {
837 log("registrationSuspended ::");
838 }
839
840 if (mListener != null) {
841 mListener.onImsSuspended();
842 }
843 }
844
845 @Override
846 public void registrationServiceCapabilityChanged(int serviceClass, int event) {
847 log("registrationServiceCapabilityChanged :: serviceClass=" +
848 serviceClass + ", event=" + event);
849
850 if (mListener != null) {
851 mListener.onImsConnected();
852 }
853 }
ram7da5a112014-07-16 20:59:27 +0530854
855 @Override
856 public void registrationFeatureCapabilityChanged(int serviceClass,
857 int[] enabledFeatures, int[] disabledFeatures) {
858 log("registrationFeatureCapabilityChanged :: serviceClass=" +
859 serviceClass);
Libin.Tang@motorola.come2296782014-08-19 14:20:01 -0500860 if (mListener != null) {
861 mListener.onFeatureCapabilityChanged(serviceClass,
862 enabledFeatures, disabledFeatures);
863 }
ram7da5a112014-07-16 20:59:27 +0530864 }
865
Wink Savilleef36ef62014-06-11 08:39:38 -0700866 }
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700867 /**
868 * Gets the ECBM interface to request ECBM exit.
869 *
870 * @param serviceId a service id which is obtained from {@link ImsManager#open}
871 * @return the ECBM interface instance
872 * @throws ImsException if getting the ECBM interface results in an error
873 */
874 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
875 if (mEcbm == null) {
876 checkAndThrowExceptionIfServiceUnavailable();
877
878 try {
879 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
880
881 if (iEcbm == null) {
882 throw new ImsException("getEcbmInterface()",
883 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
884 }
885 mEcbm = new ImsEcbm(iEcbm);
886 } catch (RemoteException e) {
887 throw new ImsException("getEcbmInterface()", e,
888 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
889 }
890 }
891 return mEcbm;
892 }
Wink Savilleef36ef62014-06-11 08:39:38 -0700893}