blob: c787bbcdc7f7d171882c2814af3ce9aba1d06189 [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;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -070023import android.content.SharedPreferences;
Wink Savilleef36ef62014-06-11 08:39:38 -070024import android.os.IBinder;
Wink Savilleef36ef62014-06-11 08:39:38 -070025import android.os.Message;
Jonathan Basseri2acea6f2015-07-01 15:00:38 -070026import android.os.PersistableBundle;
Wink Savilleef36ef62014-06-11 08:39:38 -070027import android.os.RemoteException;
28import android.os.ServiceManager;
Etan Cohenaf55a402014-09-04 22:34:41 -070029import android.os.SystemProperties;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -070030import android.preference.PreferenceManager;
Etan Cohen82f78122014-12-15 10:10:14 -080031import android.provider.Settings;
32import android.telecom.TelecomManager;
Junda Liue7663c02015-06-23 11:16:26 -070033import android.telephony.CarrierConfigManager;
Wink Savilleef36ef62014-06-11 08:39:38 -070034import android.telephony.Rlog;
Etan Cohen82f78122014-12-15 10:10:14 -080035import android.telephony.SubscriptionManager;
Etan Cohencfc784d2014-08-07 18:40:31 -070036import android.telephony.TelephonyManager;
Wink Savilleef36ef62014-06-11 08:39:38 -070037
38import com.android.ims.internal.IImsCallSession;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -070039import com.android.ims.internal.IImsEcbm;
40import com.android.ims.internal.IImsEcbmListener;
Wink Savilleef36ef62014-06-11 08:39:38 -070041import com.android.ims.internal.IImsRegistrationListener;
42import com.android.ims.internal.IImsService;
43import com.android.ims.internal.IImsUt;
44import com.android.ims.internal.ImsCallSession;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -050045import com.android.ims.internal.IImsConfig;
46
Etan Cohend7727462014-07-12 14:54:10 -070047import java.util.HashMap;
48
Wink Savilleef36ef62014-06-11 08:39:38 -070049/**
50 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
51 * the operator's IMS network. This class is the starting point for any IMS actions.
52 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
53 * <p>The APIs in this class allows you to:</p>
54 *
55 * @hide
56 */
57public class ImsManager {
Etan Cohen19604c02014-08-11 14:32:57 -070058
Etan Cohenaf55a402014-09-04 22:34:41 -070059 /*
60 * Debug flag to override configuration flag
61 */
Etan Cohenb651fa52014-10-22 10:51:29 -070062 public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
63 public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenea2b5832014-10-23 18:50:35 -070064 public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
65 public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohena00c9192014-12-23 15:02:29 -080066 public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
67 public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenaf55a402014-09-04 22:34:41 -070068
Wink Savilleef36ef62014-06-11 08:39:38 -070069 /**
70 * For accessing the IMS related service.
71 * Internal use only.
72 * @hide
73 */
Etan Cohend7727462014-07-12 14:54:10 -070074 private static final String IMS_SERVICE = "ims";
Wink Savilleef36ef62014-06-11 08:39:38 -070075
76 /**
77 * The result code to be sent back with the incoming call {@link PendingIntent}.
78 * @see #open(PendingIntent, ImsConnectionStateListener)
79 */
80 public static final int INCOMING_CALL_RESULT_CODE = 101;
81
82 /**
83 * Key to retrieve the call ID from an incoming call intent.
84 * @see #open(PendingIntent, ImsConnectionStateListener)
85 */
86 public static final String EXTRA_CALL_ID = "android:imsCallID";
87
88 /**
89 * Action to broadcast when ImsService is up.
90 * Internal use only.
91 * @hide
92 */
93 public static final String ACTION_IMS_SERVICE_UP =
94 "com.android.ims.IMS_SERVICE_UP";
95
96 /**
97 * Action to broadcast when ImsService is down.
98 * Internal use only.
99 * @hide
100 */
101 public static final String ACTION_IMS_SERVICE_DOWN =
102 "com.android.ims.IMS_SERVICE_DOWN";
103
104 /**
Pavel Zhamaitsiak0c2f15c2015-03-12 15:37:54 -0700105 * Action to broadcast when ImsService registration fails.
106 * Internal use only.
107 * @hide
108 */
109 public static final String ACTION_IMS_REGISTRATION_ERROR =
110 "com.android.ims.REGISTRATION_ERROR";
111
112 /**
Etan Cohend7727462014-07-12 14:54:10 -0700113 * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
Etan Cohenabbd7882014-09-26 22:35:35 -0700114 * A long value; the phone ID corresponding to the IMS service coming up or down.
Etan Cohend7727462014-07-12 14:54:10 -0700115 * Internal use only.
116 * @hide
117 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700118 public static final String EXTRA_PHONE_ID = "android:phone_id";
Etan Cohend7727462014-07-12 14:54:10 -0700119
120 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700121 * Action for the incoming call intent for the Phone app.
122 * Internal use only.
123 * @hide
124 */
125 public static final String ACTION_IMS_INCOMING_CALL =
126 "com.android.ims.IMS_INCOMING_CALL";
127
128 /**
129 * Part of the ACTION_IMS_INCOMING_CALL intents.
130 * An integer value; service identifier obtained from {@link ImsManager#open}.
131 * Internal use only.
132 * @hide
133 */
134 public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
135
136 /**
137 * Part of the ACTION_IMS_INCOMING_CALL intents.
138 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
139 * The value "true" indicates that the incoming call is for USSD.
140 * Internal use only.
141 * @hide
142 */
143 public static final String EXTRA_USSD = "android:ussd";
144
Shriram Ganeshd3adfad2015-05-31 10:06:15 -0700145 /**
146 * Part of the ACTION_IMS_INCOMING_CALL intents.
147 * A boolean value; Flag to indicate whether the call is an unknown
148 * dialing call. Such calls are originated by sending commands (like
149 * AT commands) directly to modem without Android involvement.
150 * Even though they are not incoming calls, they are propagated
151 * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
152 * Internal use only.
153 * @hide
154 */
Anju Mathapati9c033792015-06-16 16:33:16 -0700155 public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
Shriram Ganeshd3adfad2015-05-31 10:06:15 -0700156
Wink Savilleef36ef62014-06-11 08:39:38 -0700157 private static final String TAG = "ImsManager";
158 private static final boolean DBG = true;
159
Wink Saville1e5a38a2014-10-23 10:24:46 -0700160 private static HashMap<Integer, ImsManager> sImsManagerInstances =
161 new HashMap<Integer, ImsManager>();
Etan Cohend7727462014-07-12 14:54:10 -0700162
Wink Savilleef36ef62014-06-11 08:39:38 -0700163 private Context mContext;
Etan Cohenabbd7882014-09-26 22:35:35 -0700164 private int mPhoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700165 private IImsService mImsService = null;
166 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
167 // Ut interface for the supplementary service configuration
168 private ImsUt mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500169 // Interface to get/set ims config items
170 private ImsConfig mConfig = null;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700171 private boolean mConfigUpdated = false;
172 private static final String PREF_ENABLE_VIDEO_CALLING_KEY = "enable_video_calling";
Wink Savilleef36ef62014-06-11 08:39:38 -0700173
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700174 // ECBM interface
175 private ImsEcbm mEcbm = null;
176
Wink Savilleef36ef62014-06-11 08:39:38 -0700177 /**
178 * Gets a manager instance.
179 *
180 * @param context application context for creating the manager object
Etan Cohenabbd7882014-09-26 22:35:35 -0700181 * @param phoneId the phone ID for the IMS Service
182 * @return the manager instance corresponding to the phoneId
Wink Savilleef36ef62014-06-11 08:39:38 -0700183 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700184 public static ImsManager getInstance(Context context, int phoneId) {
Etan Cohend7727462014-07-12 14:54:10 -0700185 synchronized (sImsManagerInstances) {
Etan Cohenabbd7882014-09-26 22:35:35 -0700186 if (sImsManagerInstances.containsKey(phoneId))
187 return sImsManagerInstances.get(phoneId);
Wink Savilleef36ef62014-06-11 08:39:38 -0700188
Etan Cohenabbd7882014-09-26 22:35:35 -0700189 ImsManager mgr = new ImsManager(context, phoneId);
190 sImsManagerInstances.put(phoneId, mgr);
Etan Cohend7727462014-07-12 14:54:10 -0700191
192 return mgr;
193 }
Wink Savilleef36ef62014-06-11 08:39:38 -0700194 }
195
Etan Cohen45b5f312014-08-19 15:55:08 -0700196 /**
197 * Returns the user configuration of Enhanced 4G LTE Mode setting
198 */
199 public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
Libin.Tang@motorola.com8a6bf4b2014-10-10 15:02:41 -0500200 int enabled = android.provider.Settings.Global.getInt(
201 context.getContentResolver(),
Etan Cohenb651fa52014-10-22 10:51:29 -0700202 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
Etan Cohena00c9192014-12-23 15:02:29 -0800203 return (enabled == 1) ? true : false;
Etan Cohen45b5f312014-08-19 15:55:08 -0700204 }
205
206 /**
Etan Cohen82f78122014-12-15 10:10:14 -0800207 * Change persistent Enhanced 4G LTE Mode setting
208 */
209 public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
210 int value = enabled ? 1 : 0;
211 android.provider.Settings.Global.putInt(
212 context.getContentResolver(),
213 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
214
215 if (isNonTtyOrTtyOnVolteEnabled(context)) {
216 ImsManager imsManager = ImsManager.getInstance(context,
217 SubscriptionManager.getDefaultVoicePhoneId());
218 if (imsManager != null) {
219 try {
220 imsManager.setAdvanced4GMode(enabled);
221 } catch (ImsException ie) {
222 // do nothing
223 }
224 }
225 }
226 }
227
228 /**
229 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
230 * supported.
231 */
232 public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
Junda Liue7663c02015-06-23 11:16:26 -0700233 if (getBooleanCarrierConfig(context,
234 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
Etan Cohen82f78122014-12-15 10:10:14 -0800235 return true;
236 }
237
238 return Settings.Secure.getInt(context.getContentResolver(),
239 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
240 == TelecomManager.TTY_MODE_OFF;
241 }
242
243 /**
Etan Cohenea2b5832014-10-23 18:50:35 -0700244 * Returns a platform configuration for VoLTE which may override the user setting.
Etan Cohen45b5f312014-08-19 15:55:08 -0700245 */
Etan Cohenea2b5832014-10-23 18:50:35 -0700246 public static boolean isVolteEnabledByPlatform(Context context) {
Etan Cohenb651fa52014-10-22 10:51:29 -0700247 if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
248 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
Etan Cohenaf55a402014-09-04 22:34:41 -0700249 return true;
250 }
251
Etan Cohenb5388a32014-11-26 11:57:47 -0800252 return context.getResources().getBoolean(
Junda Liue7663c02015-06-23 11:16:26 -0700253 com.android.internal.R.bool.config_device_volte_available)
254 && getBooleanCarrierConfig(context,
Junda Liu7e16d5d2015-07-22 12:23:46 -0700255 CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL);
Etan Cohenb5388a32014-11-26 11:57:47 -0800256 }
257
258 /*
259 * Indicates whether VoLTE is provisioned on device
260 */
261 public static boolean isVolteProvisionedOnDevice(Context context) {
262 boolean isProvisioned = true;
Junda Liue7663c02015-06-23 11:16:26 -0700263 if (getBooleanCarrierConfig(context,
264 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
Etan Cohenb5388a32014-11-26 11:57:47 -0800265 isProvisioned = false; // disable on any error
266 ImsManager mgr = ImsManager.getInstance(context,
Jonathan Basserid7133652015-04-07 19:54:24 -0700267 SubscriptionManager.getDefaultVoicePhoneId());
Etan Cohenb5388a32014-11-26 11:57:47 -0800268 if (mgr != null) {
269 try {
270 ImsConfig config = mgr.getConfigInterface();
271 if (config != null) {
272 isProvisioned = config.getVolteProvisioned();
273 }
274 } catch (ImsException ie) {
275 // do nothing
276 }
277 }
278 }
279
280 return isProvisioned;
Etan Cohenb651fa52014-10-22 10:51:29 -0700281 }
282
Etan Cohenea2b5832014-10-23 18:50:35 -0700283 /**
284 * Returns a platform configuration for VT which may override the user setting.
285 *
286 * Note: VT presumes that VoLTE is enabled (these are configuration settings
287 * which must be done correctly).
288 */
Etan Cohenb651fa52014-10-22 10:51:29 -0700289 public static boolean isVtEnabledByPlatform(Context context) {
Etan Cohenea2b5832014-10-23 18:50:35 -0700290 if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
291 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
292 return true;
293 }
294
Etan Cohenb651fa52014-10-22 10:51:29 -0700295 return
296 context.getResources().getBoolean(
297 com.android.internal.R.bool.config_device_vt_available) &&
Junda Liue7663c02015-06-23 11:16:26 -0700298 getBooleanCarrierConfig(context,
299 CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL);
Etan Cohen45b5f312014-08-19 15:55:08 -0700300 }
301
Etan Cohena00c9192014-12-23 15:02:29 -0800302 /**
303 * Returns the user configuration of WFC setting
304 */
305 public static boolean isWfcEnabledByUser(Context context) {
306 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
307 android.provider.Settings.Global.WFC_IMS_ENABLED,
Pavel Zhamaitsiakeb13a592015-04-27 09:44:33 -0700308 ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800309 return (enabled == 1) ? true : false;
310 }
311
312 /**
313 * Change persistent WFC enabled setting
314 */
315 public static void setWfcSetting(Context context, boolean enabled) {
316 int value = enabled ? 1 : 0;
317 android.provider.Settings.Global.putInt(context.getContentResolver(),
318 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
319
320 ImsManager imsManager = ImsManager.getInstance(context,
321 SubscriptionManager.getDefaultVoicePhoneId());
322 if (imsManager != null) {
323 try {
324 ImsConfig config = imsManager.getConfigInterface();
Etan Cohena00c9192014-12-23 15:02:29 -0800325 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
Nathan Harold3a99f782015-07-24 15:02:34 -0700326 TelephonyManager.NETWORK_TYPE_IWLAN,
Etan Cohena00c9192014-12-23 15:02:29 -0800327 enabled ? ImsConfig.FeatureValueConstants.ON
328 : ImsConfig.FeatureValueConstants.OFF, null);
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800329
330 if (enabled) {
331 imsManager.turnOnIms();
Junda Liue7663c02015-06-23 11:16:26 -0700332 } else if (getBooleanCarrierConfig(context,
333 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800334 && (!isVolteEnabledByPlatform(context)
335 || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
336 log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
337 imsManager.turnOffIms();
338 }
Pavel Zhamaitsiak9e6eca22015-03-16 15:30:53 -0700339
340 // Force IMS to register over LTE when turning off WFC
341 setWfcModeInternal(context, enabled
342 ? getWfcMode(context)
343 : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
Etan Cohena00c9192014-12-23 15:02:29 -0800344 } catch (ImsException e) {
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800345 loge("setWfcSetting(): " + e);
Etan Cohena00c9192014-12-23 15:02:29 -0800346 }
347 }
348 }
349
350 /**
351 * Returns the user configuration of WFC modem setting
352 */
353 public static int getWfcMode(Context context) {
354 int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
355 android.provider.Settings.Global.WFC_IMS_MODE,
356 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED);
357 if (DBG) log("getWfcMode - setting=" + setting);
358 return setting;
359 }
360
361 /**
362 * Returns the user configuration of WFC modem setting
363 */
364 public static void setWfcMode(Context context, int wfcMode) {
365 if (DBG) log("setWfcMode - setting=" + wfcMode);
366 android.provider.Settings.Global.putInt(context.getContentResolver(),
367 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
368
Pavel Zhamaitsiak9e6eca22015-03-16 15:30:53 -0700369 setWfcModeInternal(context, wfcMode);
370 }
371
372 private static void setWfcModeInternal(Context context, int wfcMode) {
Etan Cohena00c9192014-12-23 15:02:29 -0800373 final ImsManager imsManager = ImsManager.getInstance(context,
374 SubscriptionManager.getDefaultVoicePhoneId());
375 if (imsManager != null) {
376 final int value = wfcMode;
377 QueuedWork.singleThreadExecutor().submit(new Runnable() {
378 public void run() {
379 try {
380 imsManager.getConfigInterface().setProvisionedValue(
381 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
382 value);
383 } catch (ImsException e) {
384 // do nothing
385 }
386 }
387 });
388 }
389 }
390
391 /**
392 * Returns the user configuration of WFC roaming setting
393 */
394 public static boolean isWfcRoamingEnabledByUser(Context context) {
395 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
396 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
Pavel Zhamaitsiak28c84002015-03-10 16:42:01 -0700397 ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800398 return (enabled == 1) ? true : false;
399 }
400
401 /**
402 * Change persistent WFC roaming enabled setting
403 */
404 public static void setWfcRoamingSetting(Context context, boolean enabled) {
Etan Cohena00c9192014-12-23 15:02:29 -0800405 android.provider.Settings.Global.putInt(context.getContentResolver(),
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700406 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
407 enabled
408 ? ImsConfig.FeatureValueConstants.ON
409 : ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800410
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700411 setWfcRoamingSettingInternal(context, enabled);
412 }
413
414 private static void setWfcRoamingSettingInternal(Context context, boolean enabled) {
Etan Cohena00c9192014-12-23 15:02:29 -0800415 final ImsManager imsManager = ImsManager.getInstance(context,
416 SubscriptionManager.getDefaultVoicePhoneId());
417 if (imsManager != null) {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700418 final int value = enabled
419 ? ImsConfig.FeatureValueConstants.ON
420 : ImsConfig.FeatureValueConstants.OFF;
Etan Cohena00c9192014-12-23 15:02:29 -0800421 QueuedWork.singleThreadExecutor().submit(new Runnable() {
422 public void run() {
423 try {
424 imsManager.getConfigInterface().setProvisionedValue(
425 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
426 value);
427 } catch (ImsException e) {
428 // do nothing
429 }
430 }
431 });
432 }
433 }
434
435 /**
436 * Returns a platform configuration for WFC which may override the user
437 * setting. Note: WFC presumes that VoLTE is enabled (these are
438 * configuration settings which must be done correctly).
439 */
440 public static boolean isWfcEnabledByPlatform(Context context) {
441 if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
442 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
443 return true;
444 }
445
446 return
447 context.getResources().getBoolean(
448 com.android.internal.R.bool.config_device_wfc_ims_available) &&
Junda Liue7663c02015-06-23 11:16:26 -0700449 getBooleanCarrierConfig(context,
450 CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL);
Etan Cohena00c9192014-12-23 15:02:29 -0800451 }
452
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700453 /**
454 * Sync carrier config and user settings with ImsConfig.
455 *
456 * @param context for the manager object
457 * @param phoneId phone id
458 * @param force update
459 */
460 public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
461 final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
462 if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
463 try {
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700464 boolean turnOn = imsManager.updateVolteFeatureValue();
465 turnOn |= imsManager.updateVideoCallFeatureValue();
466 turnOn |= imsManager.updateWfcFeatureAndProvisionedValues();
467
468 if (turnOn) {
469 imsManager.turnOnIms();
470 } else if (getBooleanCarrierConfig(context,
471 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)) {
472 imsManager.turnOffIms();
473 }
474
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700475 imsManager.mConfigUpdated = true;
476 } catch (ImsException e) {
477 loge("updateImsServiceConfig: " + e);
478 imsManager.mConfigUpdated = false;
479 }
480 }
481 }
482
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700483 /**
484 * Update VoLTE config
485 * @return whether feature is On
486 * @throws ImsException
487 */
488 private boolean updateVolteFeatureValue() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700489 boolean available = isVolteEnabledByPlatform(mContext);
490 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext);
491 boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700492 boolean turnOn = available && enabled && isNonTty;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700493
494 log("updateVolteFeatureValue: available = " + available
495 + ", enabled = " + enabled
496 + ", nonTTY = " + isNonTty);
497
498 getConfigInterface().setFeatureValue(
499 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
500 TelephonyManager.NETWORK_TYPE_LTE,
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700501 turnOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700502 ImsConfig.FeatureValueConstants.ON :
503 ImsConfig.FeatureValueConstants.OFF,
504 null);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700505
506 return turnOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700507 }
508
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700509 /**
510 * Update VC config
511 * @return whether feature is On
512 * @throws ImsException
513 */
514 private boolean updateVideoCallFeatureValue() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700515 boolean available = isVtEnabledByPlatform(mContext);
516 SharedPreferences sharedPrefs =
517 PreferenceManager.getDefaultSharedPreferences(mContext);
518 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
519 sharedPrefs.getBoolean(PREF_ENABLE_VIDEO_CALLING_KEY, true);
520 boolean isNonTty = Settings.Secure.getInt(mContext.getContentResolver(),
521 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
522 == TelecomManager.TTY_MODE_OFF;
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700523 boolean turnOn = available && enabled && isNonTty;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700524
525 log("updateVideoCallFeatureValue: available = " + available
526 + ", enabled = " + enabled
527 + ", nonTTY = " + isNonTty);
528
529 getConfigInterface().setFeatureValue(
530 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
531 TelephonyManager.NETWORK_TYPE_LTE,
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700532 turnOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700533 ImsConfig.FeatureValueConstants.ON :
534 ImsConfig.FeatureValueConstants.OFF,
535 null);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700536
537 return turnOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700538 }
539
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700540 /**
541 * Update WFC config
542 * @return whether feature is On
543 * @throws ImsException
544 */
545 private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700546 boolean available = isWfcEnabledByPlatform(mContext);
547 boolean enabled = isWfcEnabledByUser(mContext);
548 int mode = getWfcMode(mContext);
549 boolean roaming = isWfcRoamingEnabledByUser(mContext);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700550 boolean turnOn = available && enabled;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700551
552 log("updateWfcFeatureAndProvisionedValues: available = " + available
553 + ", enabled = " + enabled
554 + ", mode = " + mode
555 + ", roaming = " + roaming);
556
557 getConfigInterface().setFeatureValue(
558 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
559 TelephonyManager.NETWORK_TYPE_LTE,
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700560 turnOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700561 ImsConfig.FeatureValueConstants.ON :
562 ImsConfig.FeatureValueConstants.OFF,
563 null);
564
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700565 if (!turnOn) {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700566 mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
567 roaming = false;
568 }
569 setWfcModeInternal(mContext, mode);
570 setWfcRoamingSettingInternal(mContext, roaming);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700571
572 return turnOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700573 }
574
Etan Cohenabbd7882014-09-26 22:35:35 -0700575 private ImsManager(Context context, int phoneId) {
Wink Savilleef36ef62014-06-11 08:39:38 -0700576 mContext = context;
Etan Cohenabbd7882014-09-26 22:35:35 -0700577 mPhoneId = phoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700578 createImsService(true);
579 }
580
Etan Cohenf4311122015-02-26 17:47:13 -0800581 /*
582 * Returns a flag indicating whether the IMS service is available.
583 */
584 public boolean isServiceAvailable() {
585 if (mImsService != null) {
586 return true;
587 }
588
589 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
590 if (binder != null) {
591 return true;
592 }
593
594 return false;
595 }
596
Wink Savilleef36ef62014-06-11 08:39:38 -0700597 /**
598 * Opens the IMS service for making calls and/or receiving generic IMS calls.
599 * The caller may make subsquent calls through {@link #makeCall}.
600 * The IMS service will register the device to the operator's network with the credentials
601 * (from ISIM) periodically in order to receive calls from the operator's network.
602 * When the IMS service receives a new call, it will send out an intent with
603 * the provided action string.
604 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
605 *
606 * @param serviceClass a service class specified in {@link ImsServiceClass}
607 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
608 * @param incomingCallPendingIntent When an incoming call is received,
609 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
610 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
611 * as the result code and the intent to fill in the call ID; It cannot be null
612 * @param listener To listen to IMS registration events; It cannot be null
613 * @return identifier (greater than 0) for the specified service
614 * @throws NullPointerException if {@code incomingCallPendingIntent}
615 * or {@code listener} is null
616 * @throws ImsException if calling the IMS service results in an error
617 * @see #getCallId
618 * @see #getServiceId
619 */
620 public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
621 ImsConnectionStateListener listener) throws ImsException {
622 checkAndThrowExceptionIfServiceUnavailable();
623
624 if (incomingCallPendingIntent == null) {
625 throw new NullPointerException("incomingCallPendingIntent can't be null");
626 }
627
628 if (listener == null) {
629 throw new NullPointerException("listener can't be null");
630 }
631
632 int result = 0;
633
634 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700635 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
Wink Savilleef36ef62014-06-11 08:39:38 -0700636 createRegistrationListenerProxy(serviceClass, listener));
637 } catch (RemoteException e) {
638 throw new ImsException("open()", e,
639 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
640 }
641
642 if (result <= 0) {
643 // If the return value is a minus value,
644 // it means that an error occurred in the service.
645 // So, it needs to convert to the reason code specified in ImsReasonInfo.
646 throw new ImsException("open()", (result * (-1)));
647 }
648
649 return result;
650 }
651
652 /**
653 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
654 * All the resources that were allocated to the service are also released.
655 *
656 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
657 * @throws ImsException if calling the IMS service results in an error
658 */
659 public void close(int serviceId) throws ImsException {
660 checkAndThrowExceptionIfServiceUnavailable();
661
662 try {
663 mImsService.close(serviceId);
664 } catch (RemoteException e) {
665 throw new ImsException("close()", e,
666 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
667 } finally {
668 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500669 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700670 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700671 }
672 }
673
674 /**
675 * Gets the configuration interface to provision / withdraw the supplementary service settings.
676 *
677 * @param serviceId a service id which is obtained from {@link ImsManager#open}
678 * @return the Ut interface instance
679 * @throws ImsException if getting the Ut interface results in an error
680 */
681 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
682 throws ImsException {
683 // FIXME: manage the multiple Ut interfaces based on the service id
684 if (mUt == null) {
685 checkAndThrowExceptionIfServiceUnavailable();
686
687 try {
688 IImsUt iUt = mImsService.getUtInterface(serviceId);
689
690 if (iUt == null) {
691 throw new ImsException("getSupplementaryServiceConfiguration()",
692 ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
693 }
694
695 mUt = new ImsUt(iUt);
696 } catch (RemoteException e) {
697 throw new ImsException("getSupplementaryServiceConfiguration()", e,
698 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
699 }
700 }
701
702 return mUt;
703 }
704
705 /**
706 * Checks if the IMS service has successfully registered to the IMS network
707 * with the specified service & call type.
708 *
709 * @param serviceId a service id which is obtained from {@link ImsManager#open}
710 * @param serviceType a service type that is specified in {@link ImsCallProfile}
711 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
712 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
713 * @param callType a call type that is specified in {@link ImsCallProfile}
714 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
715 * {@link ImsCallProfile#CALL_TYPE_VOICE}
716 * {@link ImsCallProfile#CALL_TYPE_VT}
717 * {@link ImsCallProfile#CALL_TYPE_VS}
718 * @return true if the specified service id is connected to the IMS network;
719 * false otherwise
720 * @throws ImsException if calling the IMS service results in an error
721 */
722 public boolean isConnected(int serviceId, int serviceType, int callType)
723 throws ImsException {
724 checkAndThrowExceptionIfServiceUnavailable();
725
726 try {
727 return mImsService.isConnected(serviceId, serviceType, callType);
728 } catch (RemoteException e) {
729 throw new ImsException("isServiceConnected()", e,
730 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
731 }
732 }
733
734 /**
735 * Checks if the specified IMS service is opend.
736 *
737 * @param serviceId a service id which is obtained from {@link ImsManager#open}
738 * @return true if the specified service id is opened; false otherwise
739 * @throws ImsException if calling the IMS service results in an error
740 */
741 public boolean isOpened(int serviceId) throws ImsException {
742 checkAndThrowExceptionIfServiceUnavailable();
743
744 try {
745 return mImsService.isOpened(serviceId);
746 } catch (RemoteException e) {
747 throw new ImsException("isOpened()", e,
748 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
749 }
750 }
751
752 /**
753 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
754 *
755 * @param serviceId a service id which is obtained from {@link ImsManager#open}
756 * @param serviceType a service type that is specified in {@link ImsCallProfile}
757 * {@link ImsCallProfile#SERVICE_TYPE_NONE}
758 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
759 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
760 * @param callType a call type that is specified in {@link ImsCallProfile}
761 * {@link ImsCallProfile#CALL_TYPE_VOICE}
762 * {@link ImsCallProfile#CALL_TYPE_VT}
763 * {@link ImsCallProfile#CALL_TYPE_VT_TX}
764 * {@link ImsCallProfile#CALL_TYPE_VT_RX}
765 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
766 * {@link ImsCallProfile#CALL_TYPE_VS}
767 * {@link ImsCallProfile#CALL_TYPE_VS_TX}
768 * {@link ImsCallProfile#CALL_TYPE_VS_RX}
769 * @return a {@link ImsCallProfile} object
770 * @throws ImsException if calling the IMS service results in an error
771 */
772 public ImsCallProfile createCallProfile(int serviceId,
773 int serviceType, int callType) throws ImsException {
774 checkAndThrowExceptionIfServiceUnavailable();
775
776 try {
777 return mImsService.createCallProfile(serviceId, serviceType, callType);
778 } catch (RemoteException e) {
779 throw new ImsException("createCallProfile()", e,
780 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
781 }
782 }
783
784 /**
785 * Creates a {@link ImsCall} to make a call.
786 *
787 * @param serviceId a service id which is obtained from {@link ImsManager#open}
788 * @param profile a call profile to make the call
789 * (it contains service type, call type, media information, etc.)
790 * @param participants participants to invite the conference call
791 * @param listener listen to the call events from {@link ImsCall}
792 * @return a {@link ImsCall} object
793 * @throws ImsException if calling the IMS service results in an error
794 */
795 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
796 ImsCall.Listener listener) throws ImsException {
797 if (DBG) {
798 log("makeCall :: serviceId=" + serviceId
799 + ", profile=" + profile + ", callees=" + callees);
800 }
801
802 checkAndThrowExceptionIfServiceUnavailable();
803
804 ImsCall call = new ImsCall(mContext, profile);
805
806 call.setListener(listener);
807 ImsCallSession session = createCallSession(serviceId, profile);
808
809 if ((callees != null) && (callees.length == 1)) {
810 call.start(session, callees[0]);
811 } else {
812 call.start(session, callees);
813 }
814
815 return call;
816 }
817
818 /**
819 * Creates a {@link ImsCall} to take an incoming call.
820 *
821 * @param serviceId a service id which is obtained from {@link ImsManager#open}
822 * @param incomingCallIntent the incoming call broadcast intent
823 * @param listener to listen to the call events from {@link ImsCall}
824 * @return a {@link ImsCall} object
825 * @throws ImsException if calling the IMS service results in an error
826 */
827 public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
828 ImsCall.Listener listener) throws ImsException {
829 if (DBG) {
830 log("takeCall :: serviceId=" + serviceId
831 + ", incomingCall=" + incomingCallIntent);
832 }
833
834 checkAndThrowExceptionIfServiceUnavailable();
835
836 if (incomingCallIntent == null) {
837 throw new ImsException("Can't retrieve session with null intent",
838 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
839 }
840
841 int incomingServiceId = getServiceId(incomingCallIntent);
842
843 if (serviceId != incomingServiceId) {
844 throw new ImsException("Service id is mismatched in the incoming call intent",
845 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
846 }
847
848 String callId = getCallId(incomingCallIntent);
849
850 if (callId == null) {
851 throw new ImsException("Call ID missing in the incoming call intent",
852 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
853 }
854
855 try {
856 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
857
858 if (session == null) {
859 throw new ImsException("No pending session for the call",
860 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
861 }
862
863 ImsCall call = new ImsCall(mContext, session.getCallProfile());
864
865 call.attachSession(new ImsCallSession(session));
866 call.setListener(listener);
867
868 return call;
869 } catch (Throwable t) {
870 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
871 }
872 }
873
874 /**
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500875 * Gets the config interface to get/set service/capability parameters.
876 *
877 * @return the ImsConfig instance.
878 * @throws ImsException if getting the setting interface results in an error.
879 */
880 public ImsConfig getConfigInterface() throws ImsException {
881
882 if (mConfig == null) {
883 checkAndThrowExceptionIfServiceUnavailable();
884
885 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700886 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500887 if (config == null) {
888 throw new ImsException("getConfigInterface()",
889 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
890 }
Libin.Tang@motorola.com54953c72014-08-07 15:02:08 -0500891 mConfig = new ImsConfig(config, mContext);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500892 } catch (RemoteException e) {
893 throw new ImsException("getConfigInterface()", e,
894 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
895 }
896 }
897 if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
898 return mConfig;
899 }
900
Etan Cohen82f78122014-12-15 10:10:14 -0800901 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530902 throws ImsException {
903
Etan Cohen82f78122014-12-15 10:10:14 -0800904 checkAndThrowExceptionIfServiceUnavailable();
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530905
Etan Cohen82f78122014-12-15 10:10:14 -0800906 try {
907 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
908 } catch (RemoteException e) {
909 throw new ImsException("setTTYMode()", e,
910 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
911 }
912
Junda Liue7663c02015-06-23 11:16:26 -0700913 if (!getBooleanCarrierConfig(context,
914 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
Etan Cohen82f78122014-12-15 10:10:14 -0800915 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
916 isEnhanced4gLteModeSettingEnabledByUser(context));
917 }
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530918 }
919
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500920 /**
Junda Liue7663c02015-06-23 11:16:26 -0700921 * Get the boolean config from carrier config manager.
922 *
923 * @param context the context to get carrier service
924 * @param key config key defined in CarrierConfigManager
925 * @return boolean value of corresponding key.
926 */
927 private static boolean getBooleanCarrierConfig(Context context, String key) {
928 CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
929 Context.CARRIER_CONFIG_SERVICE);
Jonathan Basseri2acea6f2015-07-01 15:00:38 -0700930 PersistableBundle b = null;
Junda Liue7663c02015-06-23 11:16:26 -0700931 if (configManager != null) {
Jonathan Basseri2acea6f2015-07-01 15:00:38 -0700932 b = configManager.getConfig();
933 }
934 if (b != null) {
935 return b.getBoolean(key);
Junda Liue7663c02015-06-23 11:16:26 -0700936 } else {
937 // Return static default defined in CarrierConfigManager.
938 return CarrierConfigManager.getDefaultConfig().getBoolean(key);
939 }
940 }
941
942 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700943 * Gets the call ID from the specified incoming call broadcast intent.
944 *
945 * @param incomingCallIntent the incoming call broadcast intent
946 * @return the call ID or null if the intent does not contain it
947 */
948 private static String getCallId(Intent incomingCallIntent) {
949 if (incomingCallIntent == null) {
950 return null;
951 }
952
953 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
954 }
955
956 /**
957 * Gets the service type from the specified incoming call broadcast intent.
958 *
959 * @param incomingCallIntent the incoming call broadcast intent
960 * @return the service identifier or -1 if the intent does not contain it
961 */
962 private static int getServiceId(Intent incomingCallIntent) {
963 if (incomingCallIntent == null) {
964 return (-1);
965 }
966
967 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
968 }
969
970 /**
971 * Binds the IMS service only if the service is not created.
972 */
973 private void checkAndThrowExceptionIfServiceUnavailable()
974 throws ImsException {
975 if (mImsService == null) {
976 createImsService(true);
977
978 if (mImsService == null) {
979 throw new ImsException("Service is unavailable",
980 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
981 }
982 }
983 }
984
Etan Cohenabbd7882014-09-26 22:35:35 -0700985 private static String getImsServiceName(int phoneId) {
986 // TODO: MSIM implementation needs to decide on service name as a function of phoneId
Etan Cohend7727462014-07-12 14:54:10 -0700987 return IMS_SERVICE;
988 }
989
Wink Savilleef36ef62014-06-11 08:39:38 -0700990 /**
991 * Binds the IMS service to make/receive the call.
992 */
993 private void createImsService(boolean checkService) {
994 if (checkService) {
Etan Cohenabbd7882014-09-26 22:35:35 -0700995 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -0700996
997 if (binder == null) {
998 return;
999 }
1000 }
1001
Etan Cohenabbd7882014-09-26 22:35:35 -07001002 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -07001003
1004 if (b != null) {
1005 try {
1006 b.linkToDeath(mDeathRecipient, 0);
1007 } catch (RemoteException e) {
1008 }
1009 }
1010
1011 mImsService = IImsService.Stub.asInterface(b);
1012 }
1013
1014 /**
1015 * Creates a {@link ImsCallSession} with the specified call profile.
1016 * Use other methods, if applicable, instead of interacting with
1017 * {@link ImsCallSession} directly.
1018 *
1019 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1020 * @param profile a call profile to make the call
1021 */
1022 private ImsCallSession createCallSession(int serviceId,
1023 ImsCallProfile profile) throws ImsException {
1024 try {
1025 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
1026 } catch (RemoteException e) {
1027 return null;
1028 }
1029 }
1030
1031 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
1032 ImsConnectionStateListener listener) {
1033 ImsRegistrationListenerProxy proxy =
1034 new ImsRegistrationListenerProxy(serviceClass, listener);
1035 return proxy;
1036 }
1037
Etan Cohena00c9192014-12-23 15:02:29 -08001038 private static void log(String s) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001039 Rlog.d(TAG, s);
1040 }
1041
Etan Cohena00c9192014-12-23 15:02:29 -08001042 private static void loge(String s) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001043 Rlog.e(TAG, s);
1044 }
1045
Etan Cohena00c9192014-12-23 15:02:29 -08001046 private static void loge(String s, Throwable t) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001047 Rlog.e(TAG, s, t);
1048 }
1049
1050 /**
ram7da5a112014-07-16 20:59:27 +05301051 * Used for turning on IMS.if its off already
1052 */
Etan Cohen82f78122014-12-15 10:10:14 -08001053 private void turnOnIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -07001054 checkAndThrowExceptionIfServiceUnavailable();
1055
ram7da5a112014-07-16 20:59:27 +05301056 try {
Etan Cohenabbd7882014-09-26 22:35:35 -07001057 mImsService.turnOnIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +05301058 } catch (RemoteException e) {
1059 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1060 }
1061 }
1062
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001063 private boolean isImsTurnOffAllowed() {
1064 return getBooleanCarrierConfig(mContext,
1065 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
1066 && (!isWfcEnabledByPlatform(mContext)
1067 || !isWfcEnabledByUser(mContext));
1068 }
1069
Etan Cohen82f78122014-12-15 10:10:14 -08001070 private void setAdvanced4GMode(boolean turnOn) throws ImsException {
Etan Cohencfc784d2014-08-07 18:40:31 -07001071 checkAndThrowExceptionIfServiceUnavailable();
1072
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001073 try {
1074 ImsConfig config = getConfigInterface();
1075 if (config != null && (turnOn || !isImsTurnOffAllowed())) {
1076 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
Etan Cohenb651fa52014-10-22 10:51:29 -07001077 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001078 if (isVtEnabledByPlatform(mContext)) {
1079 // TODO: once VT is available on platform:
1080 // - replace the '1' with the current user configuration of VT.
1081 // - separate exception checks for setFeatureValue() failures for VoLTE and VT.
1082 // I.e. if VoLTE fails still try to configure VT.
1083 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
1084 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
1085 }
Etan Cohenb651fa52014-10-22 10:51:29 -07001086 }
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001087 } catch (ImsException e) {
1088 log("setAdvanced4GMode() : " + e);
Etan Cohencfc784d2014-08-07 18:40:31 -07001089 }
Etan Cohencfc784d2014-08-07 18:40:31 -07001090 if (turnOn) {
1091 turnOnIms();
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001092 } else if (isImsTurnOffAllowed()) {
Etan Cohencfc784d2014-08-07 18:40:31 -07001093 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms");
1094 turnOffIms();
1095 }
1096 }
1097
ram7da5a112014-07-16 20:59:27 +05301098 /**
1099 * Used for turning off IMS completely in order to make the device CSFB'ed.
1100 * Once turned off, all calls will be over CS.
1101 */
Etan Cohen82f78122014-12-15 10:10:14 -08001102 private void turnOffIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -07001103 checkAndThrowExceptionIfServiceUnavailable();
1104
ram7da5a112014-07-16 20:59:27 +05301105 try {
Etan Cohenabbd7882014-09-26 22:35:35 -07001106 mImsService.turnOffIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +05301107 } catch (RemoteException e) {
1108 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1109 }
1110 }
1111
1112 /**
Wink Savilleef36ef62014-06-11 08:39:38 -07001113 * Death recipient class for monitoring IMS service.
1114 */
1115 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
1116 @Override
1117 public void binderDied() {
1118 mImsService = null;
1119 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -05001120 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -07001121 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -07001122
1123 if (mContext != null) {
Etan Cohend7727462014-07-12 14:54:10 -07001124 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
Etan Cohenabbd7882014-09-26 22:35:35 -07001125 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
Etan Cohend7727462014-07-12 14:54:10 -07001126 mContext.sendBroadcast(new Intent(intent));
Wink Savilleef36ef62014-06-11 08:39:38 -07001127 }
1128 }
1129 }
1130
1131 /**
1132 * Adapter class for {@link IImsRegistrationListener}.
1133 */
1134 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
1135 private int mServiceClass;
1136 private ImsConnectionStateListener mListener;
1137
1138 public ImsRegistrationListenerProxy(int serviceClass,
1139 ImsConnectionStateListener listener) {
1140 mServiceClass = serviceClass;
1141 mListener = listener;
1142 }
1143
1144 public boolean isSameProxy(int serviceClass) {
1145 return (mServiceClass == serviceClass);
1146 }
1147
1148 @Override
1149 public void registrationConnected() {
1150 if (DBG) {
1151 log("registrationConnected ::");
1152 }
1153
1154 if (mListener != null) {
1155 mListener.onImsConnected();
1156 }
1157 }
1158
1159 @Override
Rekha Kumar14631742015-02-04 10:47:00 -08001160 public void registrationProgressing() {
Wink Savilleef36ef62014-06-11 08:39:38 -07001161 if (DBG) {
Rekha Kumar14631742015-02-04 10:47:00 -08001162 log("registrationProgressing ::");
Wink Savilleef36ef62014-06-11 08:39:38 -07001163 }
1164
1165 if (mListener != null) {
Rekha Kumar14631742015-02-04 10:47:00 -08001166 mListener.onImsProgressing();
1167 }
1168 }
1169
1170 @Override
1171 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
1172 if (DBG) {
1173 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
1174 }
1175
1176 if (mListener != null) {
1177 mListener.onImsDisconnected(imsReasonInfo);
Wink Savilleef36ef62014-06-11 08:39:38 -07001178 }
1179 }
1180
1181 @Override
1182 public void registrationResumed() {
1183 if (DBG) {
1184 log("registrationResumed ::");
1185 }
1186
1187 if (mListener != null) {
1188 mListener.onImsResumed();
1189 }
1190 }
1191
1192 @Override
1193 public void registrationSuspended() {
1194 if (DBG) {
1195 log("registrationSuspended ::");
1196 }
1197
1198 if (mListener != null) {
1199 mListener.onImsSuspended();
1200 }
1201 }
1202
1203 @Override
1204 public void registrationServiceCapabilityChanged(int serviceClass, int event) {
1205 log("registrationServiceCapabilityChanged :: serviceClass=" +
1206 serviceClass + ", event=" + event);
1207
1208 if (mListener != null) {
1209 mListener.onImsConnected();
1210 }
1211 }
ram7da5a112014-07-16 20:59:27 +05301212
1213 @Override
1214 public void registrationFeatureCapabilityChanged(int serviceClass,
1215 int[] enabledFeatures, int[] disabledFeatures) {
1216 log("registrationFeatureCapabilityChanged :: serviceClass=" +
1217 serviceClass);
Libin.Tang@motorola.come2296782014-08-19 14:20:01 -05001218 if (mListener != null) {
1219 mListener.onFeatureCapabilityChanged(serviceClass,
1220 enabledFeatures, disabledFeatures);
1221 }
ram7da5a112014-07-16 20:59:27 +05301222 }
1223
Shriram Ganeshd3adfad2015-05-31 10:06:15 -07001224 @Override
1225 public void voiceMessageCountUpdate(int count) {
1226 log("voiceMessageCountUpdate :: count=" + count);
1227
1228 if (mListener != null) {
1229 mListener.onVoiceMessageCountChanged(count);
1230 }
1231 }
1232
Wink Savilleef36ef62014-06-11 08:39:38 -07001233 }
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -07001234 /**
1235 * Gets the ECBM interface to request ECBM exit.
1236 *
1237 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1238 * @return the ECBM interface instance
1239 * @throws ImsException if getting the ECBM interface results in an error
1240 */
1241 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
1242 if (mEcbm == null) {
1243 checkAndThrowExceptionIfServiceUnavailable();
1244
1245 try {
1246 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
1247
1248 if (iEcbm == null) {
1249 throw new ImsException("getEcbmInterface()",
1250 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
1251 }
1252 mEcbm = new ImsEcbm(iEcbm);
1253 } catch (RemoteException e) {
1254 throw new ImsException("getEcbmInterface()", e,
1255 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1256 }
1257 }
1258 return mEcbm;
1259 }
Wink Savilleef36ef62014-06-11 08:39:38 -07001260}