blob: ed1a43afbfceb37d1ace7dcbbf899c7ec0db2d06 [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;
Etan Cohena00c9192014-12-23 15:02:29 -080020import android.app.QueuedWork;
Wink Savilleef36ef62014-06-11 08:39:38 -070021import android.content.Context;
22import android.content.Intent;
Wink Savilleef36ef62014-06-11 08:39:38 -070023import android.os.IBinder;
Wink Savilleef36ef62014-06-11 08:39:38 -070024import android.os.Message;
Wink Savilleef36ef62014-06-11 08:39:38 -070025import android.os.RemoteException;
26import android.os.ServiceManager;
Etan Cohenaf55a402014-09-04 22:34:41 -070027import android.os.SystemProperties;
Etan Cohen82f78122014-12-15 10:10:14 -080028import android.provider.Settings;
29import android.telecom.TelecomManager;
Wink Savilleef36ef62014-06-11 08:39:38 -070030import android.telephony.Rlog;
Etan Cohen82f78122014-12-15 10:10:14 -080031import android.telephony.SubscriptionManager;
Etan Cohencfc784d2014-08-07 18:40:31 -070032import android.telephony.TelephonyManager;
Wink Savilleef36ef62014-06-11 08:39:38 -070033
34import com.android.ims.internal.IImsCallSession;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -070035import com.android.ims.internal.IImsEcbm;
36import com.android.ims.internal.IImsEcbmListener;
Wink Savilleef36ef62014-06-11 08:39:38 -070037import com.android.ims.internal.IImsRegistrationListener;
38import com.android.ims.internal.IImsService;
39import com.android.ims.internal.IImsUt;
40import com.android.ims.internal.ImsCallSession;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -050041import com.android.ims.internal.IImsConfig;
42
Etan Cohend7727462014-07-12 14:54:10 -070043import java.util.HashMap;
44
Wink Savilleef36ef62014-06-11 08:39:38 -070045/**
46 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
47 * the operator's IMS network. This class is the starting point for any IMS actions.
48 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
49 * <p>The APIs in this class allows you to:</p>
50 *
51 * @hide
52 */
53public class ImsManager {
Etan Cohen19604c02014-08-11 14:32:57 -070054
Etan Cohenaf55a402014-09-04 22:34:41 -070055 /*
56 * Debug flag to override configuration flag
57 */
Etan Cohenb651fa52014-10-22 10:51:29 -070058 public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
59 public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenea2b5832014-10-23 18:50:35 -070060 public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
61 public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohena00c9192014-12-23 15:02:29 -080062 public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
63 public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenaf55a402014-09-04 22:34:41 -070064
Wink Savilleef36ef62014-06-11 08:39:38 -070065 /**
66 * For accessing the IMS related service.
67 * Internal use only.
68 * @hide
69 */
Etan Cohend7727462014-07-12 14:54:10 -070070 private static final String IMS_SERVICE = "ims";
Wink Savilleef36ef62014-06-11 08:39:38 -070071
72 /**
73 * The result code to be sent back with the incoming call {@link PendingIntent}.
74 * @see #open(PendingIntent, ImsConnectionStateListener)
75 */
76 public static final int INCOMING_CALL_RESULT_CODE = 101;
77
78 /**
79 * Key to retrieve the call ID from an incoming call intent.
80 * @see #open(PendingIntent, ImsConnectionStateListener)
81 */
82 public static final String EXTRA_CALL_ID = "android:imsCallID";
83
84 /**
85 * Action to broadcast when ImsService is up.
86 * Internal use only.
87 * @hide
88 */
89 public static final String ACTION_IMS_SERVICE_UP =
90 "com.android.ims.IMS_SERVICE_UP";
91
92 /**
93 * Action to broadcast when ImsService is down.
94 * Internal use only.
95 * @hide
96 */
97 public static final String ACTION_IMS_SERVICE_DOWN =
98 "com.android.ims.IMS_SERVICE_DOWN";
99
100 /**
Pavel Zhamaitsiak0c2f15c2015-03-12 15:37:54 -0700101 * Action to broadcast when ImsService registration fails.
102 * Internal use only.
103 * @hide
104 */
105 public static final String ACTION_IMS_REGISTRATION_ERROR =
106 "com.android.ims.REGISTRATION_ERROR";
107
108 /**
Etan Cohend7727462014-07-12 14:54:10 -0700109 * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
Etan Cohenabbd7882014-09-26 22:35:35 -0700110 * A long value; the phone ID corresponding to the IMS service coming up or down.
Etan Cohend7727462014-07-12 14:54:10 -0700111 * Internal use only.
112 * @hide
113 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700114 public static final String EXTRA_PHONE_ID = "android:phone_id";
Etan Cohend7727462014-07-12 14:54:10 -0700115
116 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700117 * Action for the incoming call intent for the Phone app.
118 * Internal use only.
119 * @hide
120 */
121 public static final String ACTION_IMS_INCOMING_CALL =
122 "com.android.ims.IMS_INCOMING_CALL";
123
124 /**
125 * Part of the ACTION_IMS_INCOMING_CALL intents.
126 * An integer value; service identifier obtained from {@link ImsManager#open}.
127 * Internal use only.
128 * @hide
129 */
130 public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
131
132 /**
133 * Part of the ACTION_IMS_INCOMING_CALL intents.
134 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
135 * The value "true" indicates that the incoming call is for USSD.
136 * Internal use only.
137 * @hide
138 */
139 public static final String EXTRA_USSD = "android:ussd";
140
Wink Savilleef36ef62014-06-11 08:39:38 -0700141 private static final String TAG = "ImsManager";
142 private static final boolean DBG = true;
143
Wink Saville1e5a38a2014-10-23 10:24:46 -0700144 private static HashMap<Integer, ImsManager> sImsManagerInstances =
145 new HashMap<Integer, ImsManager>();
Etan Cohend7727462014-07-12 14:54:10 -0700146
Wink Savilleef36ef62014-06-11 08:39:38 -0700147 private Context mContext;
Etan Cohenabbd7882014-09-26 22:35:35 -0700148 private int mPhoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700149 private IImsService mImsService = null;
150 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
151 // Ut interface for the supplementary service configuration
152 private ImsUt mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500153 // Interface to get/set ims config items
154 private ImsConfig mConfig = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700155
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700156 // ECBM interface
157 private ImsEcbm mEcbm = null;
158
Wink Savilleef36ef62014-06-11 08:39:38 -0700159 /**
160 * Gets a manager instance.
161 *
162 * @param context application context for creating the manager object
Etan Cohenabbd7882014-09-26 22:35:35 -0700163 * @param phoneId the phone ID for the IMS Service
164 * @return the manager instance corresponding to the phoneId
Wink Savilleef36ef62014-06-11 08:39:38 -0700165 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700166 public static ImsManager getInstance(Context context, int phoneId) {
Etan Cohend7727462014-07-12 14:54:10 -0700167 synchronized (sImsManagerInstances) {
Etan Cohenabbd7882014-09-26 22:35:35 -0700168 if (sImsManagerInstances.containsKey(phoneId))
169 return sImsManagerInstances.get(phoneId);
Wink Savilleef36ef62014-06-11 08:39:38 -0700170
Etan Cohenabbd7882014-09-26 22:35:35 -0700171 ImsManager mgr = new ImsManager(context, phoneId);
172 sImsManagerInstances.put(phoneId, mgr);
Etan Cohend7727462014-07-12 14:54:10 -0700173
174 return mgr;
175 }
Wink Savilleef36ef62014-06-11 08:39:38 -0700176 }
177
Etan Cohen45b5f312014-08-19 15:55:08 -0700178 /**
179 * Returns the user configuration of Enhanced 4G LTE Mode setting
180 */
181 public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
Libin.Tang@motorola.com8a6bf4b2014-10-10 15:02:41 -0500182 int enabled = android.provider.Settings.Global.getInt(
183 context.getContentResolver(),
Etan Cohenb651fa52014-10-22 10:51:29 -0700184 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
Etan Cohena00c9192014-12-23 15:02:29 -0800185 return (enabled == 1) ? true : false;
Etan Cohen45b5f312014-08-19 15:55:08 -0700186 }
187
188 /**
Etan Cohen82f78122014-12-15 10:10:14 -0800189 * Change persistent Enhanced 4G LTE Mode setting
190 */
191 public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
192 int value = enabled ? 1 : 0;
193 android.provider.Settings.Global.putInt(
194 context.getContentResolver(),
195 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
196
197 if (isNonTtyOrTtyOnVolteEnabled(context)) {
198 ImsManager imsManager = ImsManager.getInstance(context,
199 SubscriptionManager.getDefaultVoicePhoneId());
200 if (imsManager != null) {
201 try {
202 imsManager.setAdvanced4GMode(enabled);
203 } catch (ImsException ie) {
204 // do nothing
205 }
206 }
207 }
208 }
209
210 /**
211 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
212 * supported.
213 */
214 public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
215 if (context.getResources().getBoolean(
216 com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
217 return true;
218 }
219
220 return Settings.Secure.getInt(context.getContentResolver(),
221 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
222 == TelecomManager.TTY_MODE_OFF;
223 }
224
225 /**
Etan Cohenea2b5832014-10-23 18:50:35 -0700226 * Returns a platform configuration for VoLTE which may override the user setting.
Etan Cohen45b5f312014-08-19 15:55:08 -0700227 */
Etan Cohenea2b5832014-10-23 18:50:35 -0700228 public static boolean isVolteEnabledByPlatform(Context context) {
Etan Cohenb651fa52014-10-22 10:51:29 -0700229 if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
230 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
Etan Cohenaf55a402014-09-04 22:34:41 -0700231 return true;
232 }
233
Etan Cohend40d4a92014-11-24 11:18:33 -0800234 boolean disabledByGlobalSetting = android.provider.Settings.Global.getInt(
235 context.getContentResolver(),
236 android.provider.Settings.Global.VOLTE_FEATURE_DISABLED, 0) == 1;
237
Etan Cohenb5388a32014-11-26 11:57:47 -0800238 return context.getResources().getBoolean(
239 com.android.internal.R.bool.config_device_volte_available) && context.getResources()
240 .getBoolean(com.android.internal.R.bool.config_carrier_volte_available)
241 && !disabledByGlobalSetting;
242 }
243
244 /*
245 * Indicates whether VoLTE is provisioned on device
246 */
247 public static boolean isVolteProvisionedOnDevice(Context context) {
248 boolean isProvisioned = true;
249 if (context.getResources().getBoolean(
250 com.android.internal.R.bool.config_carrier_volte_provisioned)) {
251 isProvisioned = false; // disable on any error
252 ImsManager mgr = ImsManager.getInstance(context,
253 SubscriptionManager.getDefaultVoiceSubId());
254 if (mgr != null) {
255 try {
256 ImsConfig config = mgr.getConfigInterface();
257 if (config != null) {
258 isProvisioned = config.getVolteProvisioned();
259 }
260 } catch (ImsException ie) {
261 // do nothing
262 }
263 }
264 }
265
266 return isProvisioned;
Etan Cohenb651fa52014-10-22 10:51:29 -0700267 }
268
Etan Cohenea2b5832014-10-23 18:50:35 -0700269 /**
270 * Returns a platform configuration for VT which may override the user setting.
271 *
272 * Note: VT presumes that VoLTE is enabled (these are configuration settings
273 * which must be done correctly).
274 */
Etan Cohenb651fa52014-10-22 10:51:29 -0700275 public static boolean isVtEnabledByPlatform(Context context) {
Etan Cohenea2b5832014-10-23 18:50:35 -0700276 if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
277 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
278 return true;
279 }
280
Etan Cohenb651fa52014-10-22 10:51:29 -0700281 return
282 context.getResources().getBoolean(
283 com.android.internal.R.bool.config_device_vt_available) &&
284 context.getResources().getBoolean(
285 com.android.internal.R.bool.config_carrier_vt_available);
Etan Cohen45b5f312014-08-19 15:55:08 -0700286 }
287
Etan Cohena00c9192014-12-23 15:02:29 -0800288 /**
289 * Returns the user configuration of WFC setting
290 */
291 public static boolean isWfcEnabledByUser(Context context) {
292 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
293 android.provider.Settings.Global.WFC_IMS_ENABLED,
294 ImsConfig.FeatureValueConstants.ON);
295 return (enabled == 1) ? true : false;
296 }
297
298 /**
299 * Change persistent WFC enabled setting
300 */
301 public static void setWfcSetting(Context context, boolean enabled) {
302 int value = enabled ? 1 : 0;
303 android.provider.Settings.Global.putInt(context.getContentResolver(),
304 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
305
306 ImsManager imsManager = ImsManager.getInstance(context,
307 SubscriptionManager.getDefaultVoicePhoneId());
308 if (imsManager != null) {
309 try {
310 ImsConfig config = imsManager.getConfigInterface();
311 // FIXME: replace NETWORK_TYPE_LTE with NETWORK_TYPE_IWLAN
312 // when available
313 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
314 TelephonyManager.NETWORK_TYPE_LTE,
315 enabled ? ImsConfig.FeatureValueConstants.ON
316 : ImsConfig.FeatureValueConstants.OFF, null);
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800317
318 if (enabled) {
319 imsManager.turnOnIms();
320 } else if (context.getResources().getBoolean(
321 com.android.internal.R.bool.imsServiceAllowTurnOff)
322 && (!isVolteEnabledByPlatform(context)
323 || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
324 log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
325 imsManager.turnOffIms();
326 }
Pavel Zhamaitsiak9e6eca22015-03-16 15:30:53 -0700327
328 // Force IMS to register over LTE when turning off WFC
329 setWfcModeInternal(context, enabled
330 ? getWfcMode(context)
331 : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
Etan Cohena00c9192014-12-23 15:02:29 -0800332 } catch (ImsException e) {
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800333 loge("setWfcSetting(): " + e);
Etan Cohena00c9192014-12-23 15:02:29 -0800334 }
335 }
336 }
337
338 /**
339 * Returns the user configuration of WFC modem setting
340 */
341 public static int getWfcMode(Context context) {
342 int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
343 android.provider.Settings.Global.WFC_IMS_MODE,
344 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED);
345 if (DBG) log("getWfcMode - setting=" + setting);
346 return setting;
347 }
348
349 /**
350 * Returns the user configuration of WFC modem setting
351 */
352 public static void setWfcMode(Context context, int wfcMode) {
353 if (DBG) log("setWfcMode - setting=" + wfcMode);
354 android.provider.Settings.Global.putInt(context.getContentResolver(),
355 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
356
Pavel Zhamaitsiak9e6eca22015-03-16 15:30:53 -0700357 setWfcModeInternal(context, wfcMode);
358 }
359
360 private static void setWfcModeInternal(Context context, int wfcMode) {
Etan Cohena00c9192014-12-23 15:02:29 -0800361 final ImsManager imsManager = ImsManager.getInstance(context,
362 SubscriptionManager.getDefaultVoicePhoneId());
363 if (imsManager != null) {
364 final int value = wfcMode;
365 QueuedWork.singleThreadExecutor().submit(new Runnable() {
366 public void run() {
367 try {
368 imsManager.getConfigInterface().setProvisionedValue(
369 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
370 value);
371 } catch (ImsException e) {
372 // do nothing
373 }
374 }
375 });
376 }
377 }
378
379 /**
380 * Returns the user configuration of WFC roaming setting
381 */
382 public static boolean isWfcRoamingEnabledByUser(Context context) {
383 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
384 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
Pavel Zhamaitsiak28c84002015-03-10 16:42:01 -0700385 ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800386 return (enabled == 1) ? true : false;
387 }
388
389 /**
390 * Change persistent WFC roaming enabled setting
391 */
392 public static void setWfcRoamingSetting(Context context, boolean enabled) {
393 final int value = enabled
394 ? ImsConfig.FeatureValueConstants.ON
395 : ImsConfig.FeatureValueConstants.OFF;
396 android.provider.Settings.Global.putInt(context.getContentResolver(),
397 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED, value);
398
399 final ImsManager imsManager = ImsManager.getInstance(context,
400 SubscriptionManager.getDefaultVoicePhoneId());
401 if (imsManager != null) {
402 QueuedWork.singleThreadExecutor().submit(new Runnable() {
403 public void run() {
404 try {
405 imsManager.getConfigInterface().setProvisionedValue(
406 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
407 value);
408 } catch (ImsException e) {
409 // do nothing
410 }
411 }
412 });
413 }
414 }
415
416 /**
417 * Returns a platform configuration for WFC which may override the user
418 * setting. Note: WFC presumes that VoLTE is enabled (these are
419 * configuration settings which must be done correctly).
420 */
421 public static boolean isWfcEnabledByPlatform(Context context) {
422 if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
423 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
424 return true;
425 }
426
427 return
428 context.getResources().getBoolean(
429 com.android.internal.R.bool.config_device_wfc_ims_available) &&
430 context.getResources().getBoolean(
431 com.android.internal.R.bool.config_carrier_wfc_ims_available);
432 }
433
Etan Cohenabbd7882014-09-26 22:35:35 -0700434 private ImsManager(Context context, int phoneId) {
Wink Savilleef36ef62014-06-11 08:39:38 -0700435 mContext = context;
Etan Cohenabbd7882014-09-26 22:35:35 -0700436 mPhoneId = phoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700437 createImsService(true);
438 }
439
Etan Cohenf4311122015-02-26 17:47:13 -0800440 /*
441 * Returns a flag indicating whether the IMS service is available.
442 */
443 public boolean isServiceAvailable() {
444 if (mImsService != null) {
445 return true;
446 }
447
448 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
449 if (binder != null) {
450 return true;
451 }
452
453 return false;
454 }
455
Wink Savilleef36ef62014-06-11 08:39:38 -0700456 /**
457 * Opens the IMS service for making calls and/or receiving generic IMS calls.
458 * The caller may make subsquent calls through {@link #makeCall}.
459 * The IMS service will register the device to the operator's network with the credentials
460 * (from ISIM) periodically in order to receive calls from the operator's network.
461 * When the IMS service receives a new call, it will send out an intent with
462 * the provided action string.
463 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
464 *
465 * @param serviceClass a service class specified in {@link ImsServiceClass}
466 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
467 * @param incomingCallPendingIntent When an incoming call is received,
468 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
469 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
470 * as the result code and the intent to fill in the call ID; It cannot be null
471 * @param listener To listen to IMS registration events; It cannot be null
472 * @return identifier (greater than 0) for the specified service
473 * @throws NullPointerException if {@code incomingCallPendingIntent}
474 * or {@code listener} is null
475 * @throws ImsException if calling the IMS service results in an error
476 * @see #getCallId
477 * @see #getServiceId
478 */
479 public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
480 ImsConnectionStateListener listener) throws ImsException {
481 checkAndThrowExceptionIfServiceUnavailable();
482
483 if (incomingCallPendingIntent == null) {
484 throw new NullPointerException("incomingCallPendingIntent can't be null");
485 }
486
487 if (listener == null) {
488 throw new NullPointerException("listener can't be null");
489 }
490
491 int result = 0;
492
493 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700494 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
Wink Savilleef36ef62014-06-11 08:39:38 -0700495 createRegistrationListenerProxy(serviceClass, listener));
496 } catch (RemoteException e) {
497 throw new ImsException("open()", e,
498 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
499 }
500
501 if (result <= 0) {
502 // If the return value is a minus value,
503 // it means that an error occurred in the service.
504 // So, it needs to convert to the reason code specified in ImsReasonInfo.
505 throw new ImsException("open()", (result * (-1)));
506 }
507
508 return result;
509 }
510
511 /**
512 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
513 * All the resources that were allocated to the service are also released.
514 *
515 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
516 * @throws ImsException if calling the IMS service results in an error
517 */
518 public void close(int serviceId) throws ImsException {
519 checkAndThrowExceptionIfServiceUnavailable();
520
521 try {
522 mImsService.close(serviceId);
523 } catch (RemoteException e) {
524 throw new ImsException("close()", e,
525 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
526 } finally {
527 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500528 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700529 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700530 }
531 }
532
533 /**
534 * Gets the configuration interface to provision / withdraw the supplementary service settings.
535 *
536 * @param serviceId a service id which is obtained from {@link ImsManager#open}
537 * @return the Ut interface instance
538 * @throws ImsException if getting the Ut interface results in an error
539 */
540 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
541 throws ImsException {
542 // FIXME: manage the multiple Ut interfaces based on the service id
543 if (mUt == null) {
544 checkAndThrowExceptionIfServiceUnavailable();
545
546 try {
547 IImsUt iUt = mImsService.getUtInterface(serviceId);
548
549 if (iUt == null) {
550 throw new ImsException("getSupplementaryServiceConfiguration()",
551 ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
552 }
553
554 mUt = new ImsUt(iUt);
555 } catch (RemoteException e) {
556 throw new ImsException("getSupplementaryServiceConfiguration()", e,
557 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
558 }
559 }
560
561 return mUt;
562 }
563
564 /**
565 * Checks if the IMS service has successfully registered to the IMS network
566 * with the specified service & call type.
567 *
568 * @param serviceId a service id which is obtained from {@link ImsManager#open}
569 * @param serviceType a service type that is specified in {@link ImsCallProfile}
570 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
571 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
572 * @param callType a call type that is specified in {@link ImsCallProfile}
573 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
574 * {@link ImsCallProfile#CALL_TYPE_VOICE}
575 * {@link ImsCallProfile#CALL_TYPE_VT}
576 * {@link ImsCallProfile#CALL_TYPE_VS}
577 * @return true if the specified service id is connected to the IMS network;
578 * false otherwise
579 * @throws ImsException if calling the IMS service results in an error
580 */
581 public boolean isConnected(int serviceId, int serviceType, int callType)
582 throws ImsException {
583 checkAndThrowExceptionIfServiceUnavailable();
584
585 try {
586 return mImsService.isConnected(serviceId, serviceType, callType);
587 } catch (RemoteException e) {
588 throw new ImsException("isServiceConnected()", e,
589 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
590 }
591 }
592
593 /**
594 * Checks if the specified IMS service is opend.
595 *
596 * @param serviceId a service id which is obtained from {@link ImsManager#open}
597 * @return true if the specified service id is opened; false otherwise
598 * @throws ImsException if calling the IMS service results in an error
599 */
600 public boolean isOpened(int serviceId) throws ImsException {
601 checkAndThrowExceptionIfServiceUnavailable();
602
603 try {
604 return mImsService.isOpened(serviceId);
605 } catch (RemoteException e) {
606 throw new ImsException("isOpened()", e,
607 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
608 }
609 }
610
611 /**
612 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
613 *
614 * @param serviceId a service id which is obtained from {@link ImsManager#open}
615 * @param serviceType a service type that is specified in {@link ImsCallProfile}
616 * {@link ImsCallProfile#SERVICE_TYPE_NONE}
617 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
618 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
619 * @param callType a call type that is specified in {@link ImsCallProfile}
620 * {@link ImsCallProfile#CALL_TYPE_VOICE}
621 * {@link ImsCallProfile#CALL_TYPE_VT}
622 * {@link ImsCallProfile#CALL_TYPE_VT_TX}
623 * {@link ImsCallProfile#CALL_TYPE_VT_RX}
624 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
625 * {@link ImsCallProfile#CALL_TYPE_VS}
626 * {@link ImsCallProfile#CALL_TYPE_VS_TX}
627 * {@link ImsCallProfile#CALL_TYPE_VS_RX}
628 * @return a {@link ImsCallProfile} object
629 * @throws ImsException if calling the IMS service results in an error
630 */
631 public ImsCallProfile createCallProfile(int serviceId,
632 int serviceType, int callType) throws ImsException {
633 checkAndThrowExceptionIfServiceUnavailable();
634
635 try {
636 return mImsService.createCallProfile(serviceId, serviceType, callType);
637 } catch (RemoteException e) {
638 throw new ImsException("createCallProfile()", e,
639 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
640 }
641 }
642
643 /**
644 * Creates a {@link ImsCall} to make a call.
645 *
646 * @param serviceId a service id which is obtained from {@link ImsManager#open}
647 * @param profile a call profile to make the call
648 * (it contains service type, call type, media information, etc.)
649 * @param participants participants to invite the conference call
650 * @param listener listen to the call events from {@link ImsCall}
651 * @return a {@link ImsCall} object
652 * @throws ImsException if calling the IMS service results in an error
653 */
654 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
655 ImsCall.Listener listener) throws ImsException {
656 if (DBG) {
657 log("makeCall :: serviceId=" + serviceId
658 + ", profile=" + profile + ", callees=" + callees);
659 }
660
661 checkAndThrowExceptionIfServiceUnavailable();
662
663 ImsCall call = new ImsCall(mContext, profile);
664
665 call.setListener(listener);
666 ImsCallSession session = createCallSession(serviceId, profile);
667
668 if ((callees != null) && (callees.length == 1)) {
669 call.start(session, callees[0]);
670 } else {
671 call.start(session, callees);
672 }
673
674 return call;
675 }
676
677 /**
678 * Creates a {@link ImsCall} to take an incoming call.
679 *
680 * @param serviceId a service id which is obtained from {@link ImsManager#open}
681 * @param incomingCallIntent the incoming call broadcast intent
682 * @param listener to listen to the call events from {@link ImsCall}
683 * @return a {@link ImsCall} object
684 * @throws ImsException if calling the IMS service results in an error
685 */
686 public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
687 ImsCall.Listener listener) throws ImsException {
688 if (DBG) {
689 log("takeCall :: serviceId=" + serviceId
690 + ", incomingCall=" + incomingCallIntent);
691 }
692
693 checkAndThrowExceptionIfServiceUnavailable();
694
695 if (incomingCallIntent == null) {
696 throw new ImsException("Can't retrieve session with null intent",
697 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
698 }
699
700 int incomingServiceId = getServiceId(incomingCallIntent);
701
702 if (serviceId != incomingServiceId) {
703 throw new ImsException("Service id is mismatched in the incoming call intent",
704 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
705 }
706
707 String callId = getCallId(incomingCallIntent);
708
709 if (callId == null) {
710 throw new ImsException("Call ID missing in the incoming call intent",
711 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
712 }
713
714 try {
715 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
716
717 if (session == null) {
718 throw new ImsException("No pending session for the call",
719 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
720 }
721
722 ImsCall call = new ImsCall(mContext, session.getCallProfile());
723
724 call.attachSession(new ImsCallSession(session));
725 call.setListener(listener);
726
727 return call;
728 } catch (Throwable t) {
729 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
730 }
731 }
732
733 /**
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500734 * Gets the config interface to get/set service/capability parameters.
735 *
736 * @return the ImsConfig instance.
737 * @throws ImsException if getting the setting interface results in an error.
738 */
739 public ImsConfig getConfigInterface() throws ImsException {
740
741 if (mConfig == null) {
742 checkAndThrowExceptionIfServiceUnavailable();
743
744 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700745 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500746 if (config == null) {
747 throw new ImsException("getConfigInterface()",
748 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
749 }
Libin.Tang@motorola.com54953c72014-08-07 15:02:08 -0500750 mConfig = new ImsConfig(config, mContext);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500751 } catch (RemoteException e) {
752 throw new ImsException("getConfigInterface()", e,
753 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
754 }
755 }
756 if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
757 return mConfig;
758 }
759
Etan Cohen82f78122014-12-15 10:10:14 -0800760 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530761 throws ImsException {
762
Etan Cohen82f78122014-12-15 10:10:14 -0800763 checkAndThrowExceptionIfServiceUnavailable();
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530764
Etan Cohen82f78122014-12-15 10:10:14 -0800765 try {
766 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
767 } catch (RemoteException e) {
768 throw new ImsException("setTTYMode()", e,
769 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
770 }
771
772 if (!context.getResources().getBoolean(
773 com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
774 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
775 isEnhanced4gLteModeSettingEnabledByUser(context));
776 }
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530777 }
778
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500779 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700780 * Gets the call ID from the specified incoming call broadcast intent.
781 *
782 * @param incomingCallIntent the incoming call broadcast intent
783 * @return the call ID or null if the intent does not contain it
784 */
785 private static String getCallId(Intent incomingCallIntent) {
786 if (incomingCallIntent == null) {
787 return null;
788 }
789
790 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
791 }
792
793 /**
794 * Gets the service type from the specified incoming call broadcast intent.
795 *
796 * @param incomingCallIntent the incoming call broadcast intent
797 * @return the service identifier or -1 if the intent does not contain it
798 */
799 private static int getServiceId(Intent incomingCallIntent) {
800 if (incomingCallIntent == null) {
801 return (-1);
802 }
803
804 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
805 }
806
807 /**
808 * Binds the IMS service only if the service is not created.
809 */
810 private void checkAndThrowExceptionIfServiceUnavailable()
811 throws ImsException {
812 if (mImsService == null) {
813 createImsService(true);
814
815 if (mImsService == null) {
816 throw new ImsException("Service is unavailable",
817 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
818 }
819 }
820 }
821
Etan Cohenabbd7882014-09-26 22:35:35 -0700822 private static String getImsServiceName(int phoneId) {
823 // TODO: MSIM implementation needs to decide on service name as a function of phoneId
Etan Cohend7727462014-07-12 14:54:10 -0700824 return IMS_SERVICE;
825 }
826
Wink Savilleef36ef62014-06-11 08:39:38 -0700827 /**
828 * Binds the IMS service to make/receive the call.
829 */
830 private void createImsService(boolean checkService) {
831 if (checkService) {
Etan Cohenabbd7882014-09-26 22:35:35 -0700832 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -0700833
834 if (binder == null) {
835 return;
836 }
837 }
838
Etan Cohenabbd7882014-09-26 22:35:35 -0700839 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -0700840
841 if (b != null) {
842 try {
843 b.linkToDeath(mDeathRecipient, 0);
844 } catch (RemoteException e) {
845 }
846 }
847
848 mImsService = IImsService.Stub.asInterface(b);
849 }
850
851 /**
852 * Creates a {@link ImsCallSession} with the specified call profile.
853 * Use other methods, if applicable, instead of interacting with
854 * {@link ImsCallSession} directly.
855 *
856 * @param serviceId a service id which is obtained from {@link ImsManager#open}
857 * @param profile a call profile to make the call
858 */
859 private ImsCallSession createCallSession(int serviceId,
860 ImsCallProfile profile) throws ImsException {
861 try {
862 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
863 } catch (RemoteException e) {
864 return null;
865 }
866 }
867
868 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
869 ImsConnectionStateListener listener) {
870 ImsRegistrationListenerProxy proxy =
871 new ImsRegistrationListenerProxy(serviceClass, listener);
872 return proxy;
873 }
874
Etan Cohena00c9192014-12-23 15:02:29 -0800875 private static void log(String s) {
Wink Savilleef36ef62014-06-11 08:39:38 -0700876 Rlog.d(TAG, s);
877 }
878
Etan Cohena00c9192014-12-23 15:02:29 -0800879 private static void loge(String s) {
Wink Savilleef36ef62014-06-11 08:39:38 -0700880 Rlog.e(TAG, s);
881 }
882
Etan Cohena00c9192014-12-23 15:02:29 -0800883 private static void loge(String s, Throwable t) {
Wink Savilleef36ef62014-06-11 08:39:38 -0700884 Rlog.e(TAG, s, t);
885 }
886
887 /**
ram7da5a112014-07-16 20:59:27 +0530888 * Used for turning on IMS.if its off already
889 */
Etan Cohen82f78122014-12-15 10:10:14 -0800890 private void turnOnIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -0700891 checkAndThrowExceptionIfServiceUnavailable();
892
ram7da5a112014-07-16 20:59:27 +0530893 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700894 mImsService.turnOnIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +0530895 } catch (RemoteException e) {
896 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
897 }
898 }
899
Etan Cohen82f78122014-12-15 10:10:14 -0800900 private void setAdvanced4GMode(boolean turnOn) throws ImsException {
Etan Cohencfc784d2014-08-07 18:40:31 -0700901 checkAndThrowExceptionIfServiceUnavailable();
902
903 ImsConfig config = getConfigInterface();
904 if (config != null) {
905 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
906 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
Etan Cohenb651fa52014-10-22 10:51:29 -0700907 if (isVtEnabledByPlatform(mContext)) {
908 // TODO: once VT is available on platform replace the '1' with the current
909 // user configuration of VT.
910 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
911 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
912 }
Etan Cohencfc784d2014-08-07 18:40:31 -0700913 }
914
915 if (turnOn) {
916 turnOnIms();
917 } else if (mContext.getResources().getBoolean(
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800918 com.android.internal.R.bool.imsServiceAllowTurnOff)
919 && (!isWfcEnabledByPlatform(mContext)
920 || !isWfcEnabledByUser(mContext))) {
Etan Cohencfc784d2014-08-07 18:40:31 -0700921 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms");
922 turnOffIms();
923 }
924 }
925
ram7da5a112014-07-16 20:59:27 +0530926 /**
927 * Used for turning off IMS completely in order to make the device CSFB'ed.
928 * Once turned off, all calls will be over CS.
929 */
Etan Cohen82f78122014-12-15 10:10:14 -0800930 private void turnOffIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -0700931 checkAndThrowExceptionIfServiceUnavailable();
932
ram7da5a112014-07-16 20:59:27 +0530933 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700934 mImsService.turnOffIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +0530935 } catch (RemoteException e) {
936 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
937 }
938 }
939
940 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700941 * Death recipient class for monitoring IMS service.
942 */
943 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
944 @Override
945 public void binderDied() {
946 mImsService = null;
947 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500948 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700949 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700950
951 if (mContext != null) {
Etan Cohend7727462014-07-12 14:54:10 -0700952 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
Etan Cohenabbd7882014-09-26 22:35:35 -0700953 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
Etan Cohend7727462014-07-12 14:54:10 -0700954 mContext.sendBroadcast(new Intent(intent));
Wink Savilleef36ef62014-06-11 08:39:38 -0700955 }
956 }
957 }
958
959 /**
960 * Adapter class for {@link IImsRegistrationListener}.
961 */
962 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
963 private int mServiceClass;
964 private ImsConnectionStateListener mListener;
965
966 public ImsRegistrationListenerProxy(int serviceClass,
967 ImsConnectionStateListener listener) {
968 mServiceClass = serviceClass;
969 mListener = listener;
970 }
971
972 public boolean isSameProxy(int serviceClass) {
973 return (mServiceClass == serviceClass);
974 }
975
976 @Override
977 public void registrationConnected() {
978 if (DBG) {
979 log("registrationConnected ::");
980 }
981
982 if (mListener != null) {
983 mListener.onImsConnected();
984 }
985 }
986
987 @Override
Rekha Kumar14631742015-02-04 10:47:00 -0800988 public void registrationProgressing() {
Wink Savilleef36ef62014-06-11 08:39:38 -0700989 if (DBG) {
Rekha Kumar14631742015-02-04 10:47:00 -0800990 log("registrationProgressing ::");
Wink Savilleef36ef62014-06-11 08:39:38 -0700991 }
992
993 if (mListener != null) {
Rekha Kumar14631742015-02-04 10:47:00 -0800994 mListener.onImsProgressing();
995 }
996 }
997
998 @Override
999 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
1000 if (DBG) {
1001 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
1002 }
1003
1004 if (mListener != null) {
1005 mListener.onImsDisconnected(imsReasonInfo);
Wink Savilleef36ef62014-06-11 08:39:38 -07001006 }
1007 }
1008
1009 @Override
1010 public void registrationResumed() {
1011 if (DBG) {
1012 log("registrationResumed ::");
1013 }
1014
1015 if (mListener != null) {
1016 mListener.onImsResumed();
1017 }
1018 }
1019
1020 @Override
1021 public void registrationSuspended() {
1022 if (DBG) {
1023 log("registrationSuspended ::");
1024 }
1025
1026 if (mListener != null) {
1027 mListener.onImsSuspended();
1028 }
1029 }
1030
1031 @Override
1032 public void registrationServiceCapabilityChanged(int serviceClass, int event) {
1033 log("registrationServiceCapabilityChanged :: serviceClass=" +
1034 serviceClass + ", event=" + event);
1035
1036 if (mListener != null) {
1037 mListener.onImsConnected();
1038 }
1039 }
ram7da5a112014-07-16 20:59:27 +05301040
1041 @Override
1042 public void registrationFeatureCapabilityChanged(int serviceClass,
1043 int[] enabledFeatures, int[] disabledFeatures) {
1044 log("registrationFeatureCapabilityChanged :: serviceClass=" +
1045 serviceClass);
Libin.Tang@motorola.come2296782014-08-19 14:20:01 -05001046 if (mListener != null) {
1047 mListener.onFeatureCapabilityChanged(serviceClass,
1048 enabledFeatures, disabledFeatures);
1049 }
ram7da5a112014-07-16 20:59:27 +05301050 }
1051
Wink Savilleef36ef62014-06-11 08:39:38 -07001052 }
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -07001053 /**
1054 * Gets the ECBM interface to request ECBM exit.
1055 *
1056 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1057 * @return the ECBM interface instance
1058 * @throws ImsException if getting the ECBM interface results in an error
1059 */
1060 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
1061 if (mEcbm == null) {
1062 checkAndThrowExceptionIfServiceUnavailable();
1063
1064 try {
1065 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
1066
1067 if (iEcbm == null) {
1068 throw new ImsException("getEcbmInterface()",
1069 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
1070 }
1071 mEcbm = new ImsEcbm(iEcbm);
1072 } catch (RemoteException e) {
1073 throw new ImsException("getEcbmInterface()", e,
1074 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1075 }
1076 }
1077 return mEcbm;
1078 }
Wink Savilleef36ef62014-06-11 08:39:38 -07001079}