blob: c43d84355b0a75ff5f9d4f1292786eb6afa5ef91 [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
Jack Yu2f102bd2015-12-28 15:31:48 -080047import java.io.FileDescriptor;
48import java.io.PrintWriter;
Etan Cohend7727462014-07-12 14:54:10 -070049import java.util.HashMap;
50
Wink Savilleef36ef62014-06-11 08:39:38 -070051/**
52 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
53 * the operator's IMS network. This class is the starting point for any IMS actions.
54 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
55 * <p>The APIs in this class allows you to:</p>
56 *
57 * @hide
58 */
59public class ImsManager {
Etan Cohen19604c02014-08-11 14:32:57 -070060
Etan Cohenaf55a402014-09-04 22:34:41 -070061 /*
62 * Debug flag to override configuration flag
63 */
Etan Cohenb651fa52014-10-22 10:51:29 -070064 public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
65 public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenea2b5832014-10-23 18:50:35 -070066 public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
67 public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohena00c9192014-12-23 15:02:29 -080068 public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
69 public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenaf55a402014-09-04 22:34:41 -070070
Wink Savilleef36ef62014-06-11 08:39:38 -070071 /**
72 * For accessing the IMS related service.
73 * Internal use only.
74 * @hide
75 */
Etan Cohend7727462014-07-12 14:54:10 -070076 private static final String IMS_SERVICE = "ims";
Wink Savilleef36ef62014-06-11 08:39:38 -070077
78 /**
79 * The result code to be sent back with the incoming call {@link PendingIntent}.
80 * @see #open(PendingIntent, ImsConnectionStateListener)
81 */
82 public static final int INCOMING_CALL_RESULT_CODE = 101;
83
84 /**
85 * Key to retrieve the call ID from an incoming call intent.
86 * @see #open(PendingIntent, ImsConnectionStateListener)
87 */
88 public static final String EXTRA_CALL_ID = "android:imsCallID";
89
90 /**
91 * Action to broadcast when ImsService is up.
92 * Internal use only.
93 * @hide
94 */
95 public static final String ACTION_IMS_SERVICE_UP =
96 "com.android.ims.IMS_SERVICE_UP";
97
98 /**
99 * Action to broadcast when ImsService is down.
100 * Internal use only.
101 * @hide
102 */
103 public static final String ACTION_IMS_SERVICE_DOWN =
104 "com.android.ims.IMS_SERVICE_DOWN";
105
106 /**
Pavel Zhamaitsiak0c2f15c2015-03-12 15:37:54 -0700107 * Action to broadcast when ImsService registration fails.
108 * Internal use only.
109 * @hide
110 */
111 public static final String ACTION_IMS_REGISTRATION_ERROR =
112 "com.android.ims.REGISTRATION_ERROR";
113
114 /**
Etan Cohend7727462014-07-12 14:54:10 -0700115 * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
Etan Cohenabbd7882014-09-26 22:35:35 -0700116 * A long value; the phone ID corresponding to the IMS service coming up or down.
Etan Cohend7727462014-07-12 14:54:10 -0700117 * Internal use only.
118 * @hide
119 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700120 public static final String EXTRA_PHONE_ID = "android:phone_id";
Etan Cohend7727462014-07-12 14:54:10 -0700121
122 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700123 * Action for the incoming call intent for the Phone app.
124 * Internal use only.
125 * @hide
126 */
127 public static final String ACTION_IMS_INCOMING_CALL =
128 "com.android.ims.IMS_INCOMING_CALL";
129
130 /**
131 * Part of the ACTION_IMS_INCOMING_CALL intents.
132 * An integer value; service identifier obtained from {@link ImsManager#open}.
133 * Internal use only.
134 * @hide
135 */
136 public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
137
138 /**
139 * Part of the ACTION_IMS_INCOMING_CALL intents.
140 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
141 * The value "true" indicates that the incoming call is for USSD.
142 * Internal use only.
143 * @hide
144 */
145 public static final String EXTRA_USSD = "android:ussd";
146
Shriram Ganeshd3adfad2015-05-31 10:06:15 -0700147 /**
148 * Part of the ACTION_IMS_INCOMING_CALL intents.
149 * A boolean value; Flag to indicate whether the call is an unknown
150 * dialing call. Such calls are originated by sending commands (like
151 * AT commands) directly to modem without Android involvement.
152 * Even though they are not incoming calls, they are propagated
153 * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
154 * Internal use only.
155 * @hide
156 */
Anju Mathapati9c033792015-06-16 16:33:16 -0700157 public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
Shriram Ganeshd3adfad2015-05-31 10:06:15 -0700158
Wink Savilleef36ef62014-06-11 08:39:38 -0700159 private static final String TAG = "ImsManager";
160 private static final boolean DBG = true;
161
Wink Saville1e5a38a2014-10-23 10:24:46 -0700162 private static HashMap<Integer, ImsManager> sImsManagerInstances =
163 new HashMap<Integer, ImsManager>();
Etan Cohend7727462014-07-12 14:54:10 -0700164
Wink Savilleef36ef62014-06-11 08:39:38 -0700165 private Context mContext;
Etan Cohenabbd7882014-09-26 22:35:35 -0700166 private int mPhoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700167 private IImsService mImsService = null;
168 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
169 // Ut interface for the supplementary service configuration
170 private ImsUt mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500171 // Interface to get/set ims config items
172 private ImsConfig mConfig = null;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700173 private boolean mConfigUpdated = false;
174 private static final String PREF_ENABLE_VIDEO_CALLING_KEY = "enable_video_calling";
Wink Savilleef36ef62014-06-11 08:39:38 -0700175
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700176 // ECBM interface
177 private ImsEcbm mEcbm = null;
178
Wink Savilleef36ef62014-06-11 08:39:38 -0700179 /**
180 * Gets a manager instance.
181 *
182 * @param context application context for creating the manager object
Etan Cohenabbd7882014-09-26 22:35:35 -0700183 * @param phoneId the phone ID for the IMS Service
184 * @return the manager instance corresponding to the phoneId
Wink Savilleef36ef62014-06-11 08:39:38 -0700185 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700186 public static ImsManager getInstance(Context context, int phoneId) {
Etan Cohend7727462014-07-12 14:54:10 -0700187 synchronized (sImsManagerInstances) {
Etan Cohenabbd7882014-09-26 22:35:35 -0700188 if (sImsManagerInstances.containsKey(phoneId))
189 return sImsManagerInstances.get(phoneId);
Wink Savilleef36ef62014-06-11 08:39:38 -0700190
Etan Cohenabbd7882014-09-26 22:35:35 -0700191 ImsManager mgr = new ImsManager(context, phoneId);
192 sImsManagerInstances.put(phoneId, mgr);
Etan Cohend7727462014-07-12 14:54:10 -0700193
194 return mgr;
195 }
Wink Savilleef36ef62014-06-11 08:39:38 -0700196 }
197
Etan Cohen45b5f312014-08-19 15:55:08 -0700198 /**
199 * Returns the user configuration of Enhanced 4G LTE Mode setting
200 */
201 public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
Libin.Tang@motorola.com8a6bf4b2014-10-10 15:02:41 -0500202 int enabled = android.provider.Settings.Global.getInt(
203 context.getContentResolver(),
Etan Cohenb651fa52014-10-22 10:51:29 -0700204 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
Etan Cohena00c9192014-12-23 15:02:29 -0800205 return (enabled == 1) ? true : false;
Etan Cohen45b5f312014-08-19 15:55:08 -0700206 }
207
208 /**
Etan Cohen82f78122014-12-15 10:10:14 -0800209 * Change persistent Enhanced 4G LTE Mode setting
210 */
211 public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
212 int value = enabled ? 1 : 0;
213 android.provider.Settings.Global.putInt(
214 context.getContentResolver(),
215 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
216
217 if (isNonTtyOrTtyOnVolteEnabled(context)) {
218 ImsManager imsManager = ImsManager.getInstance(context,
219 SubscriptionManager.getDefaultVoicePhoneId());
220 if (imsManager != null) {
221 try {
222 imsManager.setAdvanced4GMode(enabled);
223 } catch (ImsException ie) {
224 // do nothing
225 }
226 }
227 }
228 }
229
230 /**
231 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
232 * supported.
233 */
234 public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
Junda Liue7663c02015-06-23 11:16:26 -0700235 if (getBooleanCarrierConfig(context,
236 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
Etan Cohen82f78122014-12-15 10:10:14 -0800237 return true;
238 }
239
240 return Settings.Secure.getInt(context.getContentResolver(),
241 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
242 == TelecomManager.TTY_MODE_OFF;
243 }
244
245 /**
Etan Cohenea2b5832014-10-23 18:50:35 -0700246 * Returns a platform configuration for VoLTE which may override the user setting.
Etan Cohen45b5f312014-08-19 15:55:08 -0700247 */
Etan Cohenea2b5832014-10-23 18:50:35 -0700248 public static boolean isVolteEnabledByPlatform(Context context) {
Etan Cohenb651fa52014-10-22 10:51:29 -0700249 if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
250 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
Etan Cohenaf55a402014-09-04 22:34:41 -0700251 return true;
252 }
253
Etan Cohenb5388a32014-11-26 11:57:47 -0800254 return context.getResources().getBoolean(
Junda Liue7663c02015-06-23 11:16:26 -0700255 com.android.internal.R.bool.config_device_volte_available)
256 && getBooleanCarrierConfig(context,
Pavel Zhamaitsiak57911d12015-10-20 14:26:34 -0700257 CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
258 && isGbaValid(context);
Etan Cohenb5388a32014-11-26 11:57:47 -0800259 }
260
261 /*
262 * Indicates whether VoLTE is provisioned on device
263 */
264 public static boolean isVolteProvisionedOnDevice(Context context) {
265 boolean isProvisioned = true;
Junda Liue7663c02015-06-23 11:16:26 -0700266 if (getBooleanCarrierConfig(context,
267 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
Etan Cohenb5388a32014-11-26 11:57:47 -0800268 isProvisioned = false; // disable on any error
269 ImsManager mgr = ImsManager.getInstance(context,
Jonathan Basserid7133652015-04-07 19:54:24 -0700270 SubscriptionManager.getDefaultVoicePhoneId());
Etan Cohenb5388a32014-11-26 11:57:47 -0800271 if (mgr != null) {
272 try {
273 ImsConfig config = mgr.getConfigInterface();
274 if (config != null) {
275 isProvisioned = config.getVolteProvisioned();
276 }
277 } catch (ImsException ie) {
278 // do nothing
279 }
280 }
281 }
282
283 return isProvisioned;
Etan Cohenb651fa52014-10-22 10:51:29 -0700284 }
285
Etan Cohenea2b5832014-10-23 18:50:35 -0700286 /**
287 * Returns a platform configuration for VT which may override the user setting.
288 *
289 * Note: VT presumes that VoLTE is enabled (these are configuration settings
290 * which must be done correctly).
291 */
Etan Cohenb651fa52014-10-22 10:51:29 -0700292 public static boolean isVtEnabledByPlatform(Context context) {
Etan Cohenea2b5832014-10-23 18:50:35 -0700293 if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
294 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
295 return true;
296 }
297
Etan Cohenb651fa52014-10-22 10:51:29 -0700298 return
299 context.getResources().getBoolean(
300 com.android.internal.R.bool.config_device_vt_available) &&
Junda Liue7663c02015-06-23 11:16:26 -0700301 getBooleanCarrierConfig(context,
Pavel Zhamaitsiak57911d12015-10-20 14:26:34 -0700302 CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
303 isGbaValid(context);
Etan Cohen45b5f312014-08-19 15:55:08 -0700304 }
305
Etan Cohena00c9192014-12-23 15:02:29 -0800306 /**
Etan Cohen6f3737f2015-05-04 18:02:09 -0700307 * Returns the user configuration of VT setting
308 */
309 public static boolean isVtEnabledByUser(Context context) {
310 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
311 android.provider.Settings.Global.VT_IMS_ENABLED,
312 ImsConfig.FeatureValueConstants.ON);
313 return (enabled == 1) ? true : false;
314 }
315
316 /**
317 * Change persistent VT enabled setting
318 */
319 public static void setVtSetting(Context context, boolean enabled) {
320 int value = enabled ? 1 : 0;
321 android.provider.Settings.Global.putInt(context.getContentResolver(),
322 android.provider.Settings.Global.VT_IMS_ENABLED, value);
323
324 ImsManager imsManager = ImsManager.getInstance(context,
325 SubscriptionManager.getDefaultVoicePhoneId());
326 if (imsManager != null) {
327 try {
328 ImsConfig config = imsManager.getConfigInterface();
329 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
330 TelephonyManager.NETWORK_TYPE_LTE,
331 enabled ? ImsConfig.FeatureValueConstants.ON
332 : ImsConfig.FeatureValueConstants.OFF, null);
333
334 if (enabled) {
335 imsManager.turnOnIms();
336 } else if (context.getResources().getBoolean(
337 com.android.internal.R.bool.imsServiceAllowTurnOff) && (
338 !isVolteEnabledByPlatform(context)
339 || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
340 log("setVtSetting() : imsServiceAllowTurnOff -> turnOffIms");
341 imsManager.turnOffIms();
342 }
343 } catch (ImsException e) {
344 loge("setVtSetting(): " + e);
345 }
346 }
347 }
348
349 /**
Etan Cohena00c9192014-12-23 15:02:29 -0800350 * Returns the user configuration of WFC setting
351 */
352 public static boolean isWfcEnabledByUser(Context context) {
353 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
354 android.provider.Settings.Global.WFC_IMS_ENABLED,
Pavel Zhamaitsiakeb13a592015-04-27 09:44:33 -0700355 ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800356 return (enabled == 1) ? true : false;
357 }
358
359 /**
360 * Change persistent WFC enabled setting
361 */
362 public static void setWfcSetting(Context context, boolean enabled) {
363 int value = enabled ? 1 : 0;
364 android.provider.Settings.Global.putInt(context.getContentResolver(),
365 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
366
367 ImsManager imsManager = ImsManager.getInstance(context,
368 SubscriptionManager.getDefaultVoicePhoneId());
369 if (imsManager != null) {
370 try {
371 ImsConfig config = imsManager.getConfigInterface();
Etan Cohena00c9192014-12-23 15:02:29 -0800372 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
Nathan Harold3a99f782015-07-24 15:02:34 -0700373 TelephonyManager.NETWORK_TYPE_IWLAN,
Etan Cohena00c9192014-12-23 15:02:29 -0800374 enabled ? ImsConfig.FeatureValueConstants.ON
375 : ImsConfig.FeatureValueConstants.OFF, null);
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800376
377 if (enabled) {
378 imsManager.turnOnIms();
Junda Liue7663c02015-06-23 11:16:26 -0700379 } else if (getBooleanCarrierConfig(context,
380 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800381 && (!isVolteEnabledByPlatform(context)
382 || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
383 log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
384 imsManager.turnOffIms();
385 }
Pavel Zhamaitsiak9e6eca22015-03-16 15:30:53 -0700386
387 // Force IMS to register over LTE when turning off WFC
388 setWfcModeInternal(context, enabled
389 ? getWfcMode(context)
390 : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
Etan Cohena00c9192014-12-23 15:02:29 -0800391 } catch (ImsException e) {
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800392 loge("setWfcSetting(): " + e);
Etan Cohena00c9192014-12-23 15:02:29 -0800393 }
394 }
395 }
396
397 /**
398 * Returns the user configuration of WFC modem setting
399 */
400 public static int getWfcMode(Context context) {
401 int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
402 android.provider.Settings.Global.WFC_IMS_MODE,
403 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED);
404 if (DBG) log("getWfcMode - setting=" + setting);
405 return setting;
406 }
407
408 /**
409 * Returns the user configuration of WFC modem setting
410 */
411 public static void setWfcMode(Context context, int wfcMode) {
412 if (DBG) log("setWfcMode - setting=" + wfcMode);
413 android.provider.Settings.Global.putInt(context.getContentResolver(),
414 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
415
Pavel Zhamaitsiak9e6eca22015-03-16 15:30:53 -0700416 setWfcModeInternal(context, wfcMode);
417 }
418
419 private static void setWfcModeInternal(Context context, int wfcMode) {
Etan Cohena00c9192014-12-23 15:02:29 -0800420 final ImsManager imsManager = ImsManager.getInstance(context,
421 SubscriptionManager.getDefaultVoicePhoneId());
422 if (imsManager != null) {
423 final int value = wfcMode;
424 QueuedWork.singleThreadExecutor().submit(new Runnable() {
425 public void run() {
426 try {
427 imsManager.getConfigInterface().setProvisionedValue(
428 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
429 value);
430 } catch (ImsException e) {
431 // do nothing
432 }
433 }
434 });
435 }
436 }
437
438 /**
439 * Returns the user configuration of WFC roaming setting
440 */
441 public static boolean isWfcRoamingEnabledByUser(Context context) {
442 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
443 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
Pavel Zhamaitsiak28c84002015-03-10 16:42:01 -0700444 ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800445 return (enabled == 1) ? true : false;
446 }
447
448 /**
449 * Change persistent WFC roaming enabled setting
450 */
451 public static void setWfcRoamingSetting(Context context, boolean enabled) {
Etan Cohena00c9192014-12-23 15:02:29 -0800452 android.provider.Settings.Global.putInt(context.getContentResolver(),
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700453 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
454 enabled
455 ? ImsConfig.FeatureValueConstants.ON
456 : ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800457
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700458 setWfcRoamingSettingInternal(context, enabled);
459 }
460
461 private static void setWfcRoamingSettingInternal(Context context, boolean enabled) {
Etan Cohena00c9192014-12-23 15:02:29 -0800462 final ImsManager imsManager = ImsManager.getInstance(context,
463 SubscriptionManager.getDefaultVoicePhoneId());
464 if (imsManager != null) {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700465 final int value = enabled
466 ? ImsConfig.FeatureValueConstants.ON
467 : ImsConfig.FeatureValueConstants.OFF;
Etan Cohena00c9192014-12-23 15:02:29 -0800468 QueuedWork.singleThreadExecutor().submit(new Runnable() {
469 public void run() {
470 try {
471 imsManager.getConfigInterface().setProvisionedValue(
472 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
473 value);
474 } catch (ImsException e) {
475 // do nothing
476 }
477 }
478 });
479 }
480 }
481
482 /**
483 * Returns a platform configuration for WFC which may override the user
484 * setting. Note: WFC presumes that VoLTE is enabled (these are
485 * configuration settings which must be done correctly).
486 */
487 public static boolean isWfcEnabledByPlatform(Context context) {
488 if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
489 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
490 return true;
491 }
492
493 return
494 context.getResources().getBoolean(
495 com.android.internal.R.bool.config_device_wfc_ims_available) &&
Junda Liue7663c02015-06-23 11:16:26 -0700496 getBooleanCarrierConfig(context,
Pavel Zhamaitsiak57911d12015-10-20 14:26:34 -0700497 CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
498 isGbaValid(context);
499 }
500
501 /**
502 * If carrier requires that IMS is only available if GBA capable SIM is used,
503 * then this function checks GBA bit in EF IST.
504 *
505 * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
506 */
507 private static boolean isGbaValid(Context context) {
508 if (getBooleanCarrierConfig(context,
509 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
510 final TelephonyManager telephonyManager = TelephonyManager.getDefault();
511 String efIst = telephonyManager.getIsimIst();
512 if (efIst == null) {
513 loge("ISF is NULL");
514 return true;
515 }
516 boolean result = efIst != null && efIst.length() > 1 &&
517 (0x02 & (byte)efIst.charAt(1)) != 0;
518 if (DBG) log("GBA capable=" + result + ", ISF=" + efIst);
519 return result;
520 }
521 return true;
Etan Cohena00c9192014-12-23 15:02:29 -0800522 }
523
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700524 /**
525 * Sync carrier config and user settings with ImsConfig.
526 *
527 * @param context for the manager object
528 * @param phoneId phone id
529 * @param force update
530 */
531 public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
532 final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
533 if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
534 try {
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700535 boolean turnOn = imsManager.updateVolteFeatureValue();
536 turnOn |= imsManager.updateVideoCallFeatureValue();
537 turnOn |= imsManager.updateWfcFeatureAndProvisionedValues();
538
539 if (turnOn) {
540 imsManager.turnOnIms();
541 } else if (getBooleanCarrierConfig(context,
542 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)) {
543 imsManager.turnOffIms();
544 }
545
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700546 imsManager.mConfigUpdated = true;
547 } catch (ImsException e) {
548 loge("updateImsServiceConfig: " + e);
549 imsManager.mConfigUpdated = false;
550 }
551 }
552 }
553
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700554 /**
555 * Update VoLTE config
556 * @return whether feature is On
557 * @throws ImsException
558 */
559 private boolean updateVolteFeatureValue() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700560 boolean available = isVolteEnabledByPlatform(mContext);
561 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext);
562 boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700563 boolean turnOn = available && enabled && isNonTty;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700564
565 log("updateVolteFeatureValue: available = " + available
566 + ", enabled = " + enabled
567 + ", nonTTY = " + isNonTty);
568
569 getConfigInterface().setFeatureValue(
570 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
571 TelephonyManager.NETWORK_TYPE_LTE,
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700572 turnOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700573 ImsConfig.FeatureValueConstants.ON :
574 ImsConfig.FeatureValueConstants.OFF,
575 null);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700576
577 return turnOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700578 }
579
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700580 /**
581 * Update VC config
582 * @return whether feature is On
583 * @throws ImsException
584 */
585 private boolean updateVideoCallFeatureValue() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700586 boolean available = isVtEnabledByPlatform(mContext);
587 SharedPreferences sharedPrefs =
588 PreferenceManager.getDefaultSharedPreferences(mContext);
589 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
590 sharedPrefs.getBoolean(PREF_ENABLE_VIDEO_CALLING_KEY, true);
591 boolean isNonTty = Settings.Secure.getInt(mContext.getContentResolver(),
592 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
593 == TelecomManager.TTY_MODE_OFF;
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700594 boolean turnOn = available && enabled && isNonTty;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700595
596 log("updateVideoCallFeatureValue: available = " + available
597 + ", enabled = " + enabled
598 + ", nonTTY = " + isNonTty);
599
600 getConfigInterface().setFeatureValue(
601 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
602 TelephonyManager.NETWORK_TYPE_LTE,
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700603 turnOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700604 ImsConfig.FeatureValueConstants.ON :
605 ImsConfig.FeatureValueConstants.OFF,
606 null);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700607
608 return turnOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700609 }
610
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700611 /**
612 * Update WFC config
613 * @return whether feature is On
614 * @throws ImsException
615 */
616 private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700617 boolean available = isWfcEnabledByPlatform(mContext);
618 boolean enabled = isWfcEnabledByUser(mContext);
619 int mode = getWfcMode(mContext);
620 boolean roaming = isWfcRoamingEnabledByUser(mContext);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700621 boolean turnOn = available && enabled;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700622
623 log("updateWfcFeatureAndProvisionedValues: available = " + available
624 + ", enabled = " + enabled
625 + ", mode = " + mode
626 + ", roaming = " + roaming);
627
628 getConfigInterface().setFeatureValue(
Pavel Zhamaitsiake6f99432015-09-11 10:29:30 -0700629 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
630 TelephonyManager.NETWORK_TYPE_IWLAN,
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700631 turnOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700632 ImsConfig.FeatureValueConstants.ON :
633 ImsConfig.FeatureValueConstants.OFF,
634 null);
635
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700636 if (!turnOn) {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700637 mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
638 roaming = false;
639 }
640 setWfcModeInternal(mContext, mode);
641 setWfcRoamingSettingInternal(mContext, roaming);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700642
643 return turnOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700644 }
645
Etan Cohenabbd7882014-09-26 22:35:35 -0700646 private ImsManager(Context context, int phoneId) {
Wink Savilleef36ef62014-06-11 08:39:38 -0700647 mContext = context;
Etan Cohenabbd7882014-09-26 22:35:35 -0700648 mPhoneId = phoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700649 createImsService(true);
650 }
651
Etan Cohenf4311122015-02-26 17:47:13 -0800652 /*
653 * Returns a flag indicating whether the IMS service is available.
654 */
655 public boolean isServiceAvailable() {
656 if (mImsService != null) {
657 return true;
658 }
659
660 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
661 if (binder != null) {
662 return true;
663 }
664
665 return false;
666 }
667
Wink Savilleef36ef62014-06-11 08:39:38 -0700668 /**
669 * Opens the IMS service for making calls and/or receiving generic IMS calls.
670 * The caller may make subsquent calls through {@link #makeCall}.
671 * The IMS service will register the device to the operator's network with the credentials
672 * (from ISIM) periodically in order to receive calls from the operator's network.
673 * When the IMS service receives a new call, it will send out an intent with
674 * the provided action string.
675 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
676 *
677 * @param serviceClass a service class specified in {@link ImsServiceClass}
678 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
679 * @param incomingCallPendingIntent When an incoming call is received,
680 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
681 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
682 * as the result code and the intent to fill in the call ID; It cannot be null
683 * @param listener To listen to IMS registration events; It cannot be null
684 * @return identifier (greater than 0) for the specified service
685 * @throws NullPointerException if {@code incomingCallPendingIntent}
686 * or {@code listener} is null
687 * @throws ImsException if calling the IMS service results in an error
688 * @see #getCallId
689 * @see #getServiceId
690 */
691 public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
692 ImsConnectionStateListener listener) throws ImsException {
693 checkAndThrowExceptionIfServiceUnavailable();
694
695 if (incomingCallPendingIntent == null) {
696 throw new NullPointerException("incomingCallPendingIntent can't be null");
697 }
698
699 if (listener == null) {
700 throw new NullPointerException("listener can't be null");
701 }
702
703 int result = 0;
704
705 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700706 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
Wink Savilleef36ef62014-06-11 08:39:38 -0700707 createRegistrationListenerProxy(serviceClass, listener));
708 } catch (RemoteException e) {
709 throw new ImsException("open()", e,
710 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
711 }
712
713 if (result <= 0) {
714 // If the return value is a minus value,
715 // it means that an error occurred in the service.
716 // So, it needs to convert to the reason code specified in ImsReasonInfo.
717 throw new ImsException("open()", (result * (-1)));
718 }
719
720 return result;
721 }
722
723 /**
724 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
725 * All the resources that were allocated to the service are also released.
726 *
727 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
728 * @throws ImsException if calling the IMS service results in an error
729 */
730 public void close(int serviceId) throws ImsException {
731 checkAndThrowExceptionIfServiceUnavailable();
732
733 try {
734 mImsService.close(serviceId);
735 } catch (RemoteException e) {
736 throw new ImsException("close()", e,
737 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
738 } finally {
739 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500740 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700741 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700742 }
743 }
744
745 /**
746 * Gets the configuration interface to provision / withdraw the supplementary service settings.
747 *
748 * @param serviceId a service id which is obtained from {@link ImsManager#open}
749 * @return the Ut interface instance
750 * @throws ImsException if getting the Ut interface results in an error
751 */
752 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
753 throws ImsException {
754 // FIXME: manage the multiple Ut interfaces based on the service id
755 if (mUt == null) {
756 checkAndThrowExceptionIfServiceUnavailable();
757
758 try {
759 IImsUt iUt = mImsService.getUtInterface(serviceId);
760
761 if (iUt == null) {
762 throw new ImsException("getSupplementaryServiceConfiguration()",
763 ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
764 }
765
766 mUt = new ImsUt(iUt);
767 } catch (RemoteException e) {
768 throw new ImsException("getSupplementaryServiceConfiguration()", e,
769 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
770 }
771 }
772
773 return mUt;
774 }
775
776 /**
777 * Checks if the IMS service has successfully registered to the IMS network
778 * with the specified service & call type.
779 *
780 * @param serviceId a service id which is obtained from {@link ImsManager#open}
781 * @param serviceType a service type that is specified in {@link ImsCallProfile}
782 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
783 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
784 * @param callType a call type that is specified in {@link ImsCallProfile}
785 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
786 * {@link ImsCallProfile#CALL_TYPE_VOICE}
787 * {@link ImsCallProfile#CALL_TYPE_VT}
788 * {@link ImsCallProfile#CALL_TYPE_VS}
789 * @return true if the specified service id is connected to the IMS network;
790 * false otherwise
791 * @throws ImsException if calling the IMS service results in an error
792 */
793 public boolean isConnected(int serviceId, int serviceType, int callType)
794 throws ImsException {
795 checkAndThrowExceptionIfServiceUnavailable();
796
797 try {
798 return mImsService.isConnected(serviceId, serviceType, callType);
799 } catch (RemoteException e) {
800 throw new ImsException("isServiceConnected()", e,
801 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
802 }
803 }
804
805 /**
806 * Checks if the specified IMS service is opend.
807 *
808 * @param serviceId a service id which is obtained from {@link ImsManager#open}
809 * @return true if the specified service id is opened; false otherwise
810 * @throws ImsException if calling the IMS service results in an error
811 */
812 public boolean isOpened(int serviceId) throws ImsException {
813 checkAndThrowExceptionIfServiceUnavailable();
814
815 try {
816 return mImsService.isOpened(serviceId);
817 } catch (RemoteException e) {
818 throw new ImsException("isOpened()", e,
819 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
820 }
821 }
822
823 /**
824 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
825 *
826 * @param serviceId a service id which is obtained from {@link ImsManager#open}
827 * @param serviceType a service type that is specified in {@link ImsCallProfile}
828 * {@link ImsCallProfile#SERVICE_TYPE_NONE}
829 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
830 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
831 * @param callType a call type that is specified in {@link ImsCallProfile}
832 * {@link ImsCallProfile#CALL_TYPE_VOICE}
833 * {@link ImsCallProfile#CALL_TYPE_VT}
834 * {@link ImsCallProfile#CALL_TYPE_VT_TX}
835 * {@link ImsCallProfile#CALL_TYPE_VT_RX}
836 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
837 * {@link ImsCallProfile#CALL_TYPE_VS}
838 * {@link ImsCallProfile#CALL_TYPE_VS_TX}
839 * {@link ImsCallProfile#CALL_TYPE_VS_RX}
840 * @return a {@link ImsCallProfile} object
841 * @throws ImsException if calling the IMS service results in an error
842 */
843 public ImsCallProfile createCallProfile(int serviceId,
844 int serviceType, int callType) throws ImsException {
845 checkAndThrowExceptionIfServiceUnavailable();
846
847 try {
848 return mImsService.createCallProfile(serviceId, serviceType, callType);
849 } catch (RemoteException e) {
850 throw new ImsException("createCallProfile()", e,
851 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
852 }
853 }
854
855 /**
856 * Creates a {@link ImsCall} to make a call.
857 *
858 * @param serviceId a service id which is obtained from {@link ImsManager#open}
859 * @param profile a call profile to make the call
860 * (it contains service type, call type, media information, etc.)
861 * @param participants participants to invite the conference call
862 * @param listener listen to the call events from {@link ImsCall}
863 * @return a {@link ImsCall} object
864 * @throws ImsException if calling the IMS service results in an error
865 */
866 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
867 ImsCall.Listener listener) throws ImsException {
868 if (DBG) {
869 log("makeCall :: serviceId=" + serviceId
870 + ", profile=" + profile + ", callees=" + callees);
871 }
872
873 checkAndThrowExceptionIfServiceUnavailable();
874
875 ImsCall call = new ImsCall(mContext, profile);
876
877 call.setListener(listener);
878 ImsCallSession session = createCallSession(serviceId, profile);
879
880 if ((callees != null) && (callees.length == 1)) {
881 call.start(session, callees[0]);
882 } else {
883 call.start(session, callees);
884 }
885
886 return call;
887 }
888
889 /**
890 * Creates a {@link ImsCall} to take an incoming call.
891 *
892 * @param serviceId a service id which is obtained from {@link ImsManager#open}
893 * @param incomingCallIntent the incoming call broadcast intent
894 * @param listener to listen to the call events from {@link ImsCall}
895 * @return a {@link ImsCall} object
896 * @throws ImsException if calling the IMS service results in an error
897 */
898 public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
899 ImsCall.Listener listener) throws ImsException {
900 if (DBG) {
901 log("takeCall :: serviceId=" + serviceId
902 + ", incomingCall=" + incomingCallIntent);
903 }
904
905 checkAndThrowExceptionIfServiceUnavailable();
906
907 if (incomingCallIntent == null) {
908 throw new ImsException("Can't retrieve session with null intent",
909 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
910 }
911
912 int incomingServiceId = getServiceId(incomingCallIntent);
913
914 if (serviceId != incomingServiceId) {
915 throw new ImsException("Service id is mismatched in the incoming call intent",
916 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
917 }
918
919 String callId = getCallId(incomingCallIntent);
920
921 if (callId == null) {
922 throw new ImsException("Call ID missing in the incoming call intent",
923 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
924 }
925
926 try {
927 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
928
929 if (session == null) {
930 throw new ImsException("No pending session for the call",
931 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
932 }
933
934 ImsCall call = new ImsCall(mContext, session.getCallProfile());
935
936 call.attachSession(new ImsCallSession(session));
937 call.setListener(listener);
938
939 return call;
940 } catch (Throwable t) {
941 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
942 }
943 }
944
945 /**
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500946 * Gets the config interface to get/set service/capability parameters.
947 *
948 * @return the ImsConfig instance.
949 * @throws ImsException if getting the setting interface results in an error.
950 */
951 public ImsConfig getConfigInterface() throws ImsException {
952
953 if (mConfig == null) {
954 checkAndThrowExceptionIfServiceUnavailable();
955
956 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700957 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500958 if (config == null) {
959 throw new ImsException("getConfigInterface()",
960 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
961 }
Libin.Tang@motorola.com54953c72014-08-07 15:02:08 -0500962 mConfig = new ImsConfig(config, mContext);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500963 } catch (RemoteException e) {
964 throw new ImsException("getConfigInterface()", e,
965 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
966 }
967 }
968 if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
969 return mConfig;
970 }
971
Etan Cohen82f78122014-12-15 10:10:14 -0800972 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530973 throws ImsException {
974
Etan Cohen82f78122014-12-15 10:10:14 -0800975 checkAndThrowExceptionIfServiceUnavailable();
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530976
Etan Cohen82f78122014-12-15 10:10:14 -0800977 try {
978 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
979 } catch (RemoteException e) {
980 throw new ImsException("setTTYMode()", e,
981 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
982 }
983
Junda Liue7663c02015-06-23 11:16:26 -0700984 if (!getBooleanCarrierConfig(context,
985 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
Etan Cohen82f78122014-12-15 10:10:14 -0800986 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
987 isEnhanced4gLteModeSettingEnabledByUser(context));
988 }
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530989 }
990
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500991 /**
Junda Liue7663c02015-06-23 11:16:26 -0700992 * Get the boolean config from carrier config manager.
993 *
994 * @param context the context to get carrier service
995 * @param key config key defined in CarrierConfigManager
996 * @return boolean value of corresponding key.
997 */
998 private static boolean getBooleanCarrierConfig(Context context, String key) {
999 CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1000 Context.CARRIER_CONFIG_SERVICE);
Jonathan Basseri2acea6f2015-07-01 15:00:38 -07001001 PersistableBundle b = null;
Junda Liue7663c02015-06-23 11:16:26 -07001002 if (configManager != null) {
Jonathan Basseri2acea6f2015-07-01 15:00:38 -07001003 b = configManager.getConfig();
1004 }
1005 if (b != null) {
1006 return b.getBoolean(key);
Junda Liue7663c02015-06-23 11:16:26 -07001007 } else {
1008 // Return static default defined in CarrierConfigManager.
1009 return CarrierConfigManager.getDefaultConfig().getBoolean(key);
1010 }
1011 }
1012
1013 /**
Wink Savilleef36ef62014-06-11 08:39:38 -07001014 * Gets the call ID from the specified incoming call broadcast intent.
1015 *
1016 * @param incomingCallIntent the incoming call broadcast intent
1017 * @return the call ID or null if the intent does not contain it
1018 */
1019 private static String getCallId(Intent incomingCallIntent) {
1020 if (incomingCallIntent == null) {
1021 return null;
1022 }
1023
1024 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
1025 }
1026
1027 /**
1028 * Gets the service type from the specified incoming call broadcast intent.
1029 *
1030 * @param incomingCallIntent the incoming call broadcast intent
1031 * @return the service identifier or -1 if the intent does not contain it
1032 */
1033 private static int getServiceId(Intent incomingCallIntent) {
1034 if (incomingCallIntent == null) {
1035 return (-1);
1036 }
1037
1038 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
1039 }
1040
1041 /**
1042 * Binds the IMS service only if the service is not created.
1043 */
1044 private void checkAndThrowExceptionIfServiceUnavailable()
1045 throws ImsException {
1046 if (mImsService == null) {
1047 createImsService(true);
1048
1049 if (mImsService == null) {
1050 throw new ImsException("Service is unavailable",
1051 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1052 }
1053 }
1054 }
1055
Etan Cohenabbd7882014-09-26 22:35:35 -07001056 private static String getImsServiceName(int phoneId) {
1057 // TODO: MSIM implementation needs to decide on service name as a function of phoneId
Etan Cohend7727462014-07-12 14:54:10 -07001058 return IMS_SERVICE;
1059 }
1060
Wink Savilleef36ef62014-06-11 08:39:38 -07001061 /**
1062 * Binds the IMS service to make/receive the call.
1063 */
1064 private void createImsService(boolean checkService) {
1065 if (checkService) {
Etan Cohenabbd7882014-09-26 22:35:35 -07001066 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -07001067
1068 if (binder == null) {
1069 return;
1070 }
1071 }
1072
Etan Cohenabbd7882014-09-26 22:35:35 -07001073 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -07001074
1075 if (b != null) {
1076 try {
1077 b.linkToDeath(mDeathRecipient, 0);
1078 } catch (RemoteException e) {
1079 }
1080 }
1081
1082 mImsService = IImsService.Stub.asInterface(b);
1083 }
1084
1085 /**
1086 * Creates a {@link ImsCallSession} with the specified call profile.
1087 * Use other methods, if applicable, instead of interacting with
1088 * {@link ImsCallSession} directly.
1089 *
1090 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1091 * @param profile a call profile to make the call
1092 */
1093 private ImsCallSession createCallSession(int serviceId,
1094 ImsCallProfile profile) throws ImsException {
1095 try {
1096 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
1097 } catch (RemoteException e) {
1098 return null;
1099 }
1100 }
1101
1102 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
1103 ImsConnectionStateListener listener) {
1104 ImsRegistrationListenerProxy proxy =
1105 new ImsRegistrationListenerProxy(serviceClass, listener);
1106 return proxy;
1107 }
1108
Etan Cohena00c9192014-12-23 15:02:29 -08001109 private static void log(String s) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001110 Rlog.d(TAG, s);
1111 }
1112
Etan Cohena00c9192014-12-23 15:02:29 -08001113 private static void loge(String s) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001114 Rlog.e(TAG, s);
1115 }
1116
Etan Cohena00c9192014-12-23 15:02:29 -08001117 private static void loge(String s, Throwable t) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001118 Rlog.e(TAG, s, t);
1119 }
1120
1121 /**
ram7da5a112014-07-16 20:59:27 +05301122 * Used for turning on IMS.if its off already
1123 */
Etan Cohen82f78122014-12-15 10:10:14 -08001124 private void turnOnIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -07001125 checkAndThrowExceptionIfServiceUnavailable();
1126
ram7da5a112014-07-16 20:59:27 +05301127 try {
Etan Cohenabbd7882014-09-26 22:35:35 -07001128 mImsService.turnOnIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +05301129 } catch (RemoteException e) {
1130 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1131 }
1132 }
1133
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001134 private boolean isImsTurnOffAllowed() {
1135 return getBooleanCarrierConfig(mContext,
1136 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
1137 && (!isWfcEnabledByPlatform(mContext)
1138 || !isWfcEnabledByUser(mContext));
1139 }
1140
Etan Cohen82f78122014-12-15 10:10:14 -08001141 private void setAdvanced4GMode(boolean turnOn) throws ImsException {
Etan Cohencfc784d2014-08-07 18:40:31 -07001142 checkAndThrowExceptionIfServiceUnavailable();
1143
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001144 try {
1145 ImsConfig config = getConfigInterface();
1146 if (config != null && (turnOn || !isImsTurnOffAllowed())) {
1147 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
Etan Cohenb651fa52014-10-22 10:51:29 -07001148 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001149 if (isVtEnabledByPlatform(mContext)) {
1150 // TODO: once VT is available on platform:
1151 // - replace the '1' with the current user configuration of VT.
1152 // - separate exception checks for setFeatureValue() failures for VoLTE and VT.
1153 // I.e. if VoLTE fails still try to configure VT.
1154 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
1155 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
1156 }
Etan Cohenb651fa52014-10-22 10:51:29 -07001157 }
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001158 } catch (ImsException e) {
1159 log("setAdvanced4GMode() : " + e);
Etan Cohencfc784d2014-08-07 18:40:31 -07001160 }
Etan Cohencfc784d2014-08-07 18:40:31 -07001161 if (turnOn) {
1162 turnOnIms();
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001163 } else if (isImsTurnOffAllowed()) {
Etan Cohencfc784d2014-08-07 18:40:31 -07001164 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms");
1165 turnOffIms();
1166 }
1167 }
1168
ram7da5a112014-07-16 20:59:27 +05301169 /**
1170 * Used for turning off IMS completely in order to make the device CSFB'ed.
1171 * Once turned off, all calls will be over CS.
1172 */
Etan Cohen82f78122014-12-15 10:10:14 -08001173 private void turnOffIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -07001174 checkAndThrowExceptionIfServiceUnavailable();
1175
ram7da5a112014-07-16 20:59:27 +05301176 try {
Etan Cohenabbd7882014-09-26 22:35:35 -07001177 mImsService.turnOffIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +05301178 } catch (RemoteException e) {
1179 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1180 }
1181 }
1182
1183 /**
Wink Savilleef36ef62014-06-11 08:39:38 -07001184 * Death recipient class for monitoring IMS service.
1185 */
1186 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
1187 @Override
1188 public void binderDied() {
1189 mImsService = null;
1190 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -05001191 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -07001192 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -07001193
1194 if (mContext != null) {
Etan Cohend7727462014-07-12 14:54:10 -07001195 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
Etan Cohenabbd7882014-09-26 22:35:35 -07001196 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
Etan Cohend7727462014-07-12 14:54:10 -07001197 mContext.sendBroadcast(new Intent(intent));
Wink Savilleef36ef62014-06-11 08:39:38 -07001198 }
1199 }
1200 }
1201
1202 /**
1203 * Adapter class for {@link IImsRegistrationListener}.
1204 */
1205 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
1206 private int mServiceClass;
1207 private ImsConnectionStateListener mListener;
1208
1209 public ImsRegistrationListenerProxy(int serviceClass,
1210 ImsConnectionStateListener listener) {
1211 mServiceClass = serviceClass;
1212 mListener = listener;
1213 }
1214
1215 public boolean isSameProxy(int serviceClass) {
1216 return (mServiceClass == serviceClass);
1217 }
1218
1219 @Override
1220 public void registrationConnected() {
1221 if (DBG) {
1222 log("registrationConnected ::");
1223 }
1224
1225 if (mListener != null) {
1226 mListener.onImsConnected();
1227 }
1228 }
1229
1230 @Override
Rekha Kumar14631742015-02-04 10:47:00 -08001231 public void registrationProgressing() {
Wink Savilleef36ef62014-06-11 08:39:38 -07001232 if (DBG) {
Rekha Kumar14631742015-02-04 10:47:00 -08001233 log("registrationProgressing ::");
Wink Savilleef36ef62014-06-11 08:39:38 -07001234 }
1235
1236 if (mListener != null) {
Rekha Kumar14631742015-02-04 10:47:00 -08001237 mListener.onImsProgressing();
1238 }
1239 }
1240
1241 @Override
1242 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
1243 if (DBG) {
1244 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
1245 }
1246
1247 if (mListener != null) {
1248 mListener.onImsDisconnected(imsReasonInfo);
Wink Savilleef36ef62014-06-11 08:39:38 -07001249 }
1250 }
1251
1252 @Override
1253 public void registrationResumed() {
1254 if (DBG) {
1255 log("registrationResumed ::");
1256 }
1257
1258 if (mListener != null) {
1259 mListener.onImsResumed();
1260 }
1261 }
1262
1263 @Override
1264 public void registrationSuspended() {
1265 if (DBG) {
1266 log("registrationSuspended ::");
1267 }
1268
1269 if (mListener != null) {
1270 mListener.onImsSuspended();
1271 }
1272 }
1273
1274 @Override
1275 public void registrationServiceCapabilityChanged(int serviceClass, int event) {
1276 log("registrationServiceCapabilityChanged :: serviceClass=" +
1277 serviceClass + ", event=" + event);
1278
1279 if (mListener != null) {
1280 mListener.onImsConnected();
1281 }
1282 }
ram7da5a112014-07-16 20:59:27 +05301283
1284 @Override
1285 public void registrationFeatureCapabilityChanged(int serviceClass,
1286 int[] enabledFeatures, int[] disabledFeatures) {
1287 log("registrationFeatureCapabilityChanged :: serviceClass=" +
1288 serviceClass);
Libin.Tang@motorola.come2296782014-08-19 14:20:01 -05001289 if (mListener != null) {
1290 mListener.onFeatureCapabilityChanged(serviceClass,
1291 enabledFeatures, disabledFeatures);
1292 }
ram7da5a112014-07-16 20:59:27 +05301293 }
1294
Shriram Ganeshd3adfad2015-05-31 10:06:15 -07001295 @Override
1296 public void voiceMessageCountUpdate(int count) {
1297 log("voiceMessageCountUpdate :: count=" + count);
1298
1299 if (mListener != null) {
1300 mListener.onVoiceMessageCountChanged(count);
1301 }
1302 }
1303
Wink Savilleef36ef62014-06-11 08:39:38 -07001304 }
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -07001305 /**
1306 * Gets the ECBM interface to request ECBM exit.
1307 *
1308 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1309 * @return the ECBM interface instance
1310 * @throws ImsException if getting the ECBM interface results in an error
1311 */
1312 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
1313 if (mEcbm == null) {
1314 checkAndThrowExceptionIfServiceUnavailable();
1315
1316 try {
1317 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
1318
1319 if (iEcbm == null) {
1320 throw new ImsException("getEcbmInterface()",
1321 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
1322 }
1323 mEcbm = new ImsEcbm(iEcbm);
1324 } catch (RemoteException e) {
1325 throw new ImsException("getEcbmInterface()", e,
1326 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1327 }
1328 }
1329 return mEcbm;
1330 }
Pavel Zhamaitsiakd2969f52015-11-10 14:36:44 -08001331
1332 /**
1333 * Resets ImsManager settings back to factory defaults.
1334 *
1335 * @hide
1336 */
1337 public static void factoryReset(Context context) {
1338 // Set VoLTE to default
1339 android.provider.Settings.Global.putInt(context.getContentResolver(),
1340 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
1341 ImsConfig.FeatureValueConstants.ON);
1342
1343 // Set VoWiFi to default
1344 android.provider.Settings.Global.putInt(context.getContentResolver(),
1345 android.provider.Settings.Global.WFC_IMS_ENABLED,
1346 ImsConfig.FeatureValueConstants.OFF);
1347
1348 // Set VoWiFi mode to default
1349 android.provider.Settings.Global.putInt(context.getContentResolver(),
1350 android.provider.Settings.Global.WFC_IMS_MODE,
1351 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED);
1352
1353 // Set VoWiFi roaming to default
1354 android.provider.Settings.Global.putInt(context.getContentResolver(),
1355 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1356 ImsConfig.FeatureValueConstants.OFF);
1357
1358 // Set VT to default
1359 SharedPreferences sharedPrefs =
1360 PreferenceManager.getDefaultSharedPreferences(context);
1361 SharedPreferences.Editor editor = sharedPrefs.edit();
1362 editor.putBoolean(PREF_ENABLE_VIDEO_CALLING_KEY, true);
1363 editor.commit();
1364
1365 // Push settings to ImsConfig
1366 ImsManager.updateImsServiceConfig(context,
1367 SubscriptionManager.getDefaultVoicePhoneId(), true);
1368 }
Jack Yu2f102bd2015-12-28 15:31:48 -08001369
1370 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1371 pw.println("ImsManager:");
1372 pw.println(" mPhoneId = " + mPhoneId);
1373 pw.println(" mConfigUpdated = " + mConfigUpdated);
1374 pw.println(" mImsService = " + mImsService);
1375
1376 pw.println(" isGbaValid = " + isGbaValid(mContext));
1377 pw.println(" isImsTurnOffAllowed = " + isImsTurnOffAllowed());
1378 pw.println(" isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabled(mContext));
1379
1380 pw.println(" isVolteEnabledByPlatform = " + isVolteEnabledByPlatform(mContext));
1381 pw.println(" isVolteProvisionedOnDevice = " + isVolteProvisionedOnDevice(mContext));
1382 pw.println(" isEnhanced4gLteModeSettingEnabledByUser = " +
1383 isEnhanced4gLteModeSettingEnabledByUser(mContext));
1384 pw.println(" isVtEnabledByPlatform = " + isVtEnabledByPlatform(mContext));
1385 pw.println(" isVtEnabledByUser = " + isVtEnabledByUser(mContext));
1386
1387 pw.println(" isWfcEnabledByPlatform = " + isWfcEnabledByPlatform(mContext));
1388 pw.println(" isWfcEnabledByUser = " + isWfcEnabledByUser(mContext));
1389 pw.println(" getWfcMode = " + getWfcMode(mContext));
1390 pw.println(" isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUser(mContext));
1391
1392 pw.flush();
1393 }
Wink Savilleef36ef62014-06-11 08:39:38 -07001394}