blob: 4127551bdb1b1321e6fc072dd8eee7467f4371de [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;
Pavel Zhamaitsiak4de9cbb2016-02-11 17:21:05 -080024import android.net.Uri;
Wink Savilleef36ef62014-06-11 08:39:38 -070025import android.os.IBinder;
Wink Savilleef36ef62014-06-11 08:39:38 -070026import android.os.Message;
Jonathan Basseri2acea6f2015-07-01 15:00:38 -070027import android.os.PersistableBundle;
Wink Savilleef36ef62014-06-11 08:39:38 -070028import android.os.RemoteException;
29import android.os.ServiceManager;
Etan Cohenaf55a402014-09-04 22:34:41 -070030import android.os.SystemProperties;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -070031import android.preference.PreferenceManager;
Etan Cohen82f78122014-12-15 10:10:14 -080032import android.provider.Settings;
33import android.telecom.TelecomManager;
Junda Liue7663c02015-06-23 11:16:26 -070034import android.telephony.CarrierConfigManager;
Wink Savilleef36ef62014-06-11 08:39:38 -070035import android.telephony.Rlog;
Etan Cohen82f78122014-12-15 10:10:14 -080036import android.telephony.SubscriptionManager;
Etan Cohencfc784d2014-08-07 18:40:31 -070037import android.telephony.TelephonyManager;
Wink Savilleef36ef62014-06-11 08:39:38 -070038
39import com.android.ims.internal.IImsCallSession;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -070040import com.android.ims.internal.IImsEcbm;
41import com.android.ims.internal.IImsEcbmListener;
Wink Savilleef36ef62014-06-11 08:39:38 -070042import com.android.ims.internal.IImsRegistrationListener;
43import com.android.ims.internal.IImsService;
44import com.android.ims.internal.IImsUt;
45import com.android.ims.internal.ImsCallSession;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -050046import com.android.ims.internal.IImsConfig;
47
Jack Yu2f102bd2015-12-28 15:31:48 -080048import java.io.FileDescriptor;
49import java.io.PrintWriter;
Etan Cohend7727462014-07-12 14:54:10 -070050import java.util.HashMap;
51
Wink Savilleef36ef62014-06-11 08:39:38 -070052/**
53 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
54 * the operator's IMS network. This class is the starting point for any IMS actions.
55 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
56 * <p>The APIs in this class allows you to:</p>
57 *
58 * @hide
59 */
60public class ImsManager {
Etan Cohen19604c02014-08-11 14:32:57 -070061
Etan Cohenaf55a402014-09-04 22:34:41 -070062 /*
63 * Debug flag to override configuration flag
64 */
Etan Cohenb651fa52014-10-22 10:51:29 -070065 public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
66 public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenea2b5832014-10-23 18:50:35 -070067 public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
68 public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohena00c9192014-12-23 15:02:29 -080069 public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
70 public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
Etan Cohenaf55a402014-09-04 22:34:41 -070071
Wink Savilleef36ef62014-06-11 08:39:38 -070072 /**
73 * For accessing the IMS related service.
74 * Internal use only.
75 * @hide
76 */
Etan Cohend7727462014-07-12 14:54:10 -070077 private static final String IMS_SERVICE = "ims";
Wink Savilleef36ef62014-06-11 08:39:38 -070078
79 /**
80 * The result code to be sent back with the incoming call {@link PendingIntent}.
81 * @see #open(PendingIntent, ImsConnectionStateListener)
82 */
83 public static final int INCOMING_CALL_RESULT_CODE = 101;
84
85 /**
86 * Key to retrieve the call ID from an incoming call intent.
87 * @see #open(PendingIntent, ImsConnectionStateListener)
88 */
89 public static final String EXTRA_CALL_ID = "android:imsCallID";
90
91 /**
92 * Action to broadcast when ImsService is up.
93 * Internal use only.
94 * @hide
95 */
96 public static final String ACTION_IMS_SERVICE_UP =
97 "com.android.ims.IMS_SERVICE_UP";
98
99 /**
100 * Action to broadcast when ImsService is down.
101 * Internal use only.
102 * @hide
103 */
104 public static final String ACTION_IMS_SERVICE_DOWN =
105 "com.android.ims.IMS_SERVICE_DOWN";
106
107 /**
Pavel Zhamaitsiak0c2f15c2015-03-12 15:37:54 -0700108 * Action to broadcast when ImsService registration fails.
109 * Internal use only.
110 * @hide
111 */
112 public static final String ACTION_IMS_REGISTRATION_ERROR =
113 "com.android.ims.REGISTRATION_ERROR";
114
115 /**
Etan Cohend7727462014-07-12 14:54:10 -0700116 * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
Etan Cohenabbd7882014-09-26 22:35:35 -0700117 * A long value; the phone ID corresponding to the IMS service coming up or down.
Etan Cohend7727462014-07-12 14:54:10 -0700118 * Internal use only.
119 * @hide
120 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700121 public static final String EXTRA_PHONE_ID = "android:phone_id";
Etan Cohend7727462014-07-12 14:54:10 -0700122
123 /**
Wink Savilleef36ef62014-06-11 08:39:38 -0700124 * Action for the incoming call intent for the Phone app.
125 * Internal use only.
126 * @hide
127 */
128 public static final String ACTION_IMS_INCOMING_CALL =
129 "com.android.ims.IMS_INCOMING_CALL";
130
131 /**
132 * Part of the ACTION_IMS_INCOMING_CALL intents.
133 * An integer value; service identifier obtained from {@link ImsManager#open}.
134 * Internal use only.
135 * @hide
136 */
137 public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
138
139 /**
140 * Part of the ACTION_IMS_INCOMING_CALL intents.
141 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
142 * The value "true" indicates that the incoming call is for USSD.
143 * Internal use only.
144 * @hide
145 */
146 public static final String EXTRA_USSD = "android:ussd";
147
Shriram Ganeshd3adfad2015-05-31 10:06:15 -0700148 /**
149 * Part of the ACTION_IMS_INCOMING_CALL intents.
150 * A boolean value; Flag to indicate whether the call is an unknown
151 * dialing call. Such calls are originated by sending commands (like
152 * AT commands) directly to modem without Android involvement.
153 * Even though they are not incoming calls, they are propagated
154 * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
155 * Internal use only.
156 * @hide
157 */
Anju Mathapati9c033792015-06-16 16:33:16 -0700158 public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
Shriram Ganeshd3adfad2015-05-31 10:06:15 -0700159
Wink Savilleef36ef62014-06-11 08:39:38 -0700160 private static final String TAG = "ImsManager";
161 private static final boolean DBG = true;
162
Wink Saville1e5a38a2014-10-23 10:24:46 -0700163 private static HashMap<Integer, ImsManager> sImsManagerInstances =
164 new HashMap<Integer, ImsManager>();
Etan Cohend7727462014-07-12 14:54:10 -0700165
Wink Savilleef36ef62014-06-11 08:39:38 -0700166 private Context mContext;
Etan Cohenabbd7882014-09-26 22:35:35 -0700167 private int mPhoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700168 private IImsService mImsService = null;
169 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
170 // Ut interface for the supplementary service configuration
171 private ImsUt mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500172 // Interface to get/set ims config items
173 private ImsConfig mConfig = null;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700174 private boolean mConfigUpdated = false;
175 private static final String PREF_ENABLE_VIDEO_CALLING_KEY = "enable_video_calling";
Wink Savilleef36ef62014-06-11 08:39:38 -0700176
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -0800177 private ImsConfigListener mImsConfigListener;
178
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700179 // ECBM interface
180 private ImsEcbm mEcbm = null;
181
Wink Savilleef36ef62014-06-11 08:39:38 -0700182 /**
183 * Gets a manager instance.
184 *
185 * @param context application context for creating the manager object
Etan Cohenabbd7882014-09-26 22:35:35 -0700186 * @param phoneId the phone ID for the IMS Service
187 * @return the manager instance corresponding to the phoneId
Wink Savilleef36ef62014-06-11 08:39:38 -0700188 */
Etan Cohenabbd7882014-09-26 22:35:35 -0700189 public static ImsManager getInstance(Context context, int phoneId) {
Etan Cohend7727462014-07-12 14:54:10 -0700190 synchronized (sImsManagerInstances) {
Etan Cohenabbd7882014-09-26 22:35:35 -0700191 if (sImsManagerInstances.containsKey(phoneId))
192 return sImsManagerInstances.get(phoneId);
Wink Savilleef36ef62014-06-11 08:39:38 -0700193
Etan Cohenabbd7882014-09-26 22:35:35 -0700194 ImsManager mgr = new ImsManager(context, phoneId);
195 sImsManagerInstances.put(phoneId, mgr);
Etan Cohend7727462014-07-12 14:54:10 -0700196
197 return mgr;
198 }
Wink Savilleef36ef62014-06-11 08:39:38 -0700199 }
200
Etan Cohen45b5f312014-08-19 15:55:08 -0700201 /**
202 * Returns the user configuration of Enhanced 4G LTE Mode setting
203 */
204 public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
Sungmin Choi2f1af952016-02-01 17:15:35 +0900205 // If user can't edit Enhanced 4G LTE Mode, it assumes Enhanced 4G LTE Mode is always true.
206 // If user changes SIM from editable mode to uneditable mode, need to return true.
207 if (!getBooleanCarrierConfig(context,
208 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)) {
209 return true;
210 }
Libin.Tang@motorola.com8a6bf4b2014-10-10 15:02:41 -0500211 int enabled = android.provider.Settings.Global.getInt(
212 context.getContentResolver(),
Etan Cohenb651fa52014-10-22 10:51:29 -0700213 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
Etan Cohena00c9192014-12-23 15:02:29 -0800214 return (enabled == 1) ? true : false;
Etan Cohen45b5f312014-08-19 15:55:08 -0700215 }
216
217 /**
Etan Cohen82f78122014-12-15 10:10:14 -0800218 * Change persistent Enhanced 4G LTE Mode setting
219 */
220 public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
221 int value = enabled ? 1 : 0;
222 android.provider.Settings.Global.putInt(
223 context.getContentResolver(),
224 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
225
226 if (isNonTtyOrTtyOnVolteEnabled(context)) {
227 ImsManager imsManager = ImsManager.getInstance(context,
228 SubscriptionManager.getDefaultVoicePhoneId());
229 if (imsManager != null) {
230 try {
231 imsManager.setAdvanced4GMode(enabled);
232 } catch (ImsException ie) {
233 // do nothing
234 }
235 }
236 }
237 }
238
239 /**
240 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
241 * supported.
242 */
243 public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
Junda Liue7663c02015-06-23 11:16:26 -0700244 if (getBooleanCarrierConfig(context,
245 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
Etan Cohen82f78122014-12-15 10:10:14 -0800246 return true;
247 }
248
249 return Settings.Secure.getInt(context.getContentResolver(),
250 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
251 == TelecomManager.TTY_MODE_OFF;
252 }
253
254 /**
Etan Cohenea2b5832014-10-23 18:50:35 -0700255 * Returns a platform configuration for VoLTE which may override the user setting.
Etan Cohen45b5f312014-08-19 15:55:08 -0700256 */
Etan Cohenea2b5832014-10-23 18:50:35 -0700257 public static boolean isVolteEnabledByPlatform(Context context) {
Etan Cohenb651fa52014-10-22 10:51:29 -0700258 if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
259 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
Etan Cohenaf55a402014-09-04 22:34:41 -0700260 return true;
261 }
262
Etan Cohenb5388a32014-11-26 11:57:47 -0800263 return context.getResources().getBoolean(
Junda Liue7663c02015-06-23 11:16:26 -0700264 com.android.internal.R.bool.config_device_volte_available)
265 && getBooleanCarrierConfig(context,
Pavel Zhamaitsiak57911d12015-10-20 14:26:34 -0700266 CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
267 && isGbaValid(context);
Etan Cohenb5388a32014-11-26 11:57:47 -0800268 }
269
270 /*
271 * Indicates whether VoLTE is provisioned on device
272 */
273 public static boolean isVolteProvisionedOnDevice(Context context) {
274 boolean isProvisioned = true;
Junda Liue7663c02015-06-23 11:16:26 -0700275 if (getBooleanCarrierConfig(context,
276 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
Etan Cohenb5388a32014-11-26 11:57:47 -0800277 isProvisioned = false; // disable on any error
278 ImsManager mgr = ImsManager.getInstance(context,
Jonathan Basserid7133652015-04-07 19:54:24 -0700279 SubscriptionManager.getDefaultVoicePhoneId());
Etan Cohenb5388a32014-11-26 11:57:47 -0800280 if (mgr != null) {
281 try {
282 ImsConfig config = mgr.getConfigInterface();
283 if (config != null) {
284 isProvisioned = config.getVolteProvisioned();
285 }
286 } catch (ImsException ie) {
287 // do nothing
288 }
289 }
290 }
291
292 return isProvisioned;
Etan Cohenb651fa52014-10-22 10:51:29 -0700293 }
294
Etan Cohenea2b5832014-10-23 18:50:35 -0700295 /**
296 * Returns a platform configuration for VT which may override the user setting.
297 *
298 * Note: VT presumes that VoLTE is enabled (these are configuration settings
299 * which must be done correctly).
300 */
Etan Cohenb651fa52014-10-22 10:51:29 -0700301 public static boolean isVtEnabledByPlatform(Context context) {
Etan Cohenea2b5832014-10-23 18:50:35 -0700302 if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
303 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
304 return true;
305 }
306
Etan Cohenb651fa52014-10-22 10:51:29 -0700307 return
308 context.getResources().getBoolean(
309 com.android.internal.R.bool.config_device_vt_available) &&
Junda Liue7663c02015-06-23 11:16:26 -0700310 getBooleanCarrierConfig(context,
Pavel Zhamaitsiak57911d12015-10-20 14:26:34 -0700311 CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
312 isGbaValid(context);
Etan Cohen45b5f312014-08-19 15:55:08 -0700313 }
314
Etan Cohena00c9192014-12-23 15:02:29 -0800315 /**
Etan Cohena7d32e82015-05-04 18:02:09 -0700316 * Returns the user configuration of VT setting
317 */
318 public static boolean isVtEnabledByUser(Context context) {
319 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
320 android.provider.Settings.Global.VT_IMS_ENABLED,
321 ImsConfig.FeatureValueConstants.ON);
322 return (enabled == 1) ? true : false;
323 }
324
325 /**
326 * Change persistent VT enabled setting
327 */
328 public static void setVtSetting(Context context, boolean enabled) {
329 int value = enabled ? 1 : 0;
330 android.provider.Settings.Global.putInt(context.getContentResolver(),
331 android.provider.Settings.Global.VT_IMS_ENABLED, value);
332
333 ImsManager imsManager = ImsManager.getInstance(context,
334 SubscriptionManager.getDefaultVoicePhoneId());
335 if (imsManager != null) {
336 try {
337 ImsConfig config = imsManager.getConfigInterface();
338 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
339 TelephonyManager.NETWORK_TYPE_LTE,
340 enabled ? ImsConfig.FeatureValueConstants.ON
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -0800341 : ImsConfig.FeatureValueConstants.OFF,
342 imsManager.mImsConfigListener);
Etan Cohena7d32e82015-05-04 18:02:09 -0700343
344 if (enabled) {
345 imsManager.turnOnIms();
Pavel Zhamaitsiak4ca0cde2015-12-22 16:51:57 -0800346 } else if (getBooleanCarrierConfig(context,
347 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
348 && (!isVolteEnabledByPlatform(context)
Etan Cohena7d32e82015-05-04 18:02:09 -0700349 || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
350 log("setVtSetting() : imsServiceAllowTurnOff -> turnOffIms");
351 imsManager.turnOffIms();
352 }
353 } catch (ImsException e) {
354 loge("setVtSetting(): " + e);
355 }
356 }
357 }
358
359 /**
Etan Cohena00c9192014-12-23 15:02:29 -0800360 * Returns the user configuration of WFC setting
361 */
362 public static boolean isWfcEnabledByUser(Context context) {
363 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
364 android.provider.Settings.Global.WFC_IMS_ENABLED,
Pavel Zhamaitsiakeb13a592015-04-27 09:44:33 -0700365 ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800366 return (enabled == 1) ? true : false;
367 }
368
369 /**
370 * Change persistent WFC enabled setting
371 */
372 public static void setWfcSetting(Context context, boolean enabled) {
373 int value = enabled ? 1 : 0;
374 android.provider.Settings.Global.putInt(context.getContentResolver(),
375 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
376
377 ImsManager imsManager = ImsManager.getInstance(context,
378 SubscriptionManager.getDefaultVoicePhoneId());
379 if (imsManager != null) {
380 try {
381 ImsConfig config = imsManager.getConfigInterface();
Etan Cohena00c9192014-12-23 15:02:29 -0800382 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
Nathan Harold3a99f782015-07-24 15:02:34 -0700383 TelephonyManager.NETWORK_TYPE_IWLAN,
Etan Cohena00c9192014-12-23 15:02:29 -0800384 enabled ? ImsConfig.FeatureValueConstants.ON
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -0800385 : ImsConfig.FeatureValueConstants.OFF,
386 imsManager.mImsConfigListener);
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800387
388 if (enabled) {
389 imsManager.turnOnIms();
Junda Liue7663c02015-06-23 11:16:26 -0700390 } else if (getBooleanCarrierConfig(context,
391 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800392 && (!isVolteEnabledByPlatform(context)
393 || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
394 log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
395 imsManager.turnOffIms();
396 }
Pavel Zhamaitsiak9e6eca22015-03-16 15:30:53 -0700397
398 // Force IMS to register over LTE when turning off WFC
399 setWfcModeInternal(context, enabled
400 ? getWfcMode(context)
401 : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
Etan Cohena00c9192014-12-23 15:02:29 -0800402 } catch (ImsException e) {
Pavel Zhamaitsiak183af602015-02-24 10:20:27 -0800403 loge("setWfcSetting(): " + e);
Etan Cohena00c9192014-12-23 15:02:29 -0800404 }
405 }
406 }
407
408 /**
409 * Returns the user configuration of WFC modem setting
410 */
411 public static int getWfcMode(Context context) {
412 int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
413 android.provider.Settings.Global.WFC_IMS_MODE,
414 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED);
415 if (DBG) log("getWfcMode - setting=" + setting);
416 return setting;
417 }
418
419 /**
420 * Returns the user configuration of WFC modem setting
421 */
422 public static void setWfcMode(Context context, int wfcMode) {
423 if (DBG) log("setWfcMode - setting=" + wfcMode);
424 android.provider.Settings.Global.putInt(context.getContentResolver(),
425 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
426
Pavel Zhamaitsiak9e6eca22015-03-16 15:30:53 -0700427 setWfcModeInternal(context, wfcMode);
428 }
429
430 private static void setWfcModeInternal(Context context, int wfcMode) {
Etan Cohena00c9192014-12-23 15:02:29 -0800431 final ImsManager imsManager = ImsManager.getInstance(context,
432 SubscriptionManager.getDefaultVoicePhoneId());
433 if (imsManager != null) {
434 final int value = wfcMode;
435 QueuedWork.singleThreadExecutor().submit(new Runnable() {
436 public void run() {
437 try {
438 imsManager.getConfigInterface().setProvisionedValue(
439 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
440 value);
441 } catch (ImsException e) {
442 // do nothing
443 }
444 }
445 });
446 }
447 }
448
449 /**
450 * Returns the user configuration of WFC roaming setting
451 */
452 public static boolean isWfcRoamingEnabledByUser(Context context) {
453 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
454 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
Pavel Zhamaitsiak28c84002015-03-10 16:42:01 -0700455 ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800456 return (enabled == 1) ? true : false;
457 }
458
459 /**
460 * Change persistent WFC roaming enabled setting
461 */
462 public static void setWfcRoamingSetting(Context context, boolean enabled) {
Etan Cohena00c9192014-12-23 15:02:29 -0800463 android.provider.Settings.Global.putInt(context.getContentResolver(),
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700464 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
465 enabled
466 ? ImsConfig.FeatureValueConstants.ON
467 : ImsConfig.FeatureValueConstants.OFF);
Etan Cohena00c9192014-12-23 15:02:29 -0800468
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700469 setWfcRoamingSettingInternal(context, enabled);
470 }
471
472 private static void setWfcRoamingSettingInternal(Context context, boolean enabled) {
Etan Cohena00c9192014-12-23 15:02:29 -0800473 final ImsManager imsManager = ImsManager.getInstance(context,
474 SubscriptionManager.getDefaultVoicePhoneId());
475 if (imsManager != null) {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700476 final int value = enabled
477 ? ImsConfig.FeatureValueConstants.ON
478 : ImsConfig.FeatureValueConstants.OFF;
Etan Cohena00c9192014-12-23 15:02:29 -0800479 QueuedWork.singleThreadExecutor().submit(new Runnable() {
480 public void run() {
481 try {
482 imsManager.getConfigInterface().setProvisionedValue(
483 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
484 value);
485 } catch (ImsException e) {
486 // do nothing
487 }
488 }
489 });
490 }
491 }
492
493 /**
494 * Returns a platform configuration for WFC which may override the user
495 * setting. Note: WFC presumes that VoLTE is enabled (these are
496 * configuration settings which must be done correctly).
497 */
498 public static boolean isWfcEnabledByPlatform(Context context) {
499 if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
500 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
501 return true;
502 }
503
504 return
505 context.getResources().getBoolean(
506 com.android.internal.R.bool.config_device_wfc_ims_available) &&
Junda Liue7663c02015-06-23 11:16:26 -0700507 getBooleanCarrierConfig(context,
Pavel Zhamaitsiak57911d12015-10-20 14:26:34 -0700508 CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
509 isGbaValid(context);
510 }
511
512 /**
513 * If carrier requires that IMS is only available if GBA capable SIM is used,
514 * then this function checks GBA bit in EF IST.
515 *
516 * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
517 */
518 private static boolean isGbaValid(Context context) {
519 if (getBooleanCarrierConfig(context,
520 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
521 final TelephonyManager telephonyManager = TelephonyManager.getDefault();
522 String efIst = telephonyManager.getIsimIst();
523 if (efIst == null) {
524 loge("ISF is NULL");
525 return true;
526 }
527 boolean result = efIst != null && efIst.length() > 1 &&
528 (0x02 & (byte)efIst.charAt(1)) != 0;
529 if (DBG) log("GBA capable=" + result + ", ISF=" + efIst);
530 return result;
531 }
532 return true;
Etan Cohena00c9192014-12-23 15:02:29 -0800533 }
534
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700535 /**
536 * Sync carrier config and user settings with ImsConfig.
537 *
538 * @param context for the manager object
539 * @param phoneId phone id
540 * @param force update
541 */
542 public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
543 final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
544 if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
545 try {
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800546 boolean isImsUsed = imsManager.updateVolteFeatureValue();
547 isImsUsed |= imsManager.updateVideoCallFeatureValue();
548 isImsUsed |= imsManager.updateWfcFeatureAndProvisionedValues();
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700549
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800550 if (isImsUsed || !getBooleanCarrierConfig(context,
551 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)) {
552 // Turn on IMS if it is used.
553 // Also, if turning off is not allowed for current carrier,
554 // we need to turn IMS on because it might be turned off before
555 // phone switched to current carrier.
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700556 imsManager.turnOnIms();
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800557 } else {
558 // Turn off IMS if it is not used AND turning off is allowed for carrier.
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700559 imsManager.turnOffIms();
560 }
561
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700562 imsManager.mConfigUpdated = true;
563 } catch (ImsException e) {
564 loge("updateImsServiceConfig: " + e);
565 imsManager.mConfigUpdated = false;
566 }
567 }
568 }
569
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700570 /**
571 * Update VoLTE config
572 * @return whether feature is On
573 * @throws ImsException
574 */
575 private boolean updateVolteFeatureValue() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700576 boolean available = isVolteEnabledByPlatform(mContext);
577 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext);
578 boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800579 boolean isFeatureOn = available && enabled && isNonTty;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700580
581 log("updateVolteFeatureValue: available = " + available
582 + ", enabled = " + enabled
583 + ", nonTTY = " + isNonTty);
584
585 getConfigInterface().setFeatureValue(
586 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
587 TelephonyManager.NETWORK_TYPE_LTE,
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800588 isFeatureOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700589 ImsConfig.FeatureValueConstants.ON :
590 ImsConfig.FeatureValueConstants.OFF,
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -0800591 mImsConfigListener);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700592
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800593 return isFeatureOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700594 }
595
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700596 /**
597 * Update VC config
598 * @return whether feature is On
599 * @throws ImsException
600 */
601 private boolean updateVideoCallFeatureValue() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700602 boolean available = isVtEnabledByPlatform(mContext);
603 SharedPreferences sharedPrefs =
604 PreferenceManager.getDefaultSharedPreferences(mContext);
605 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
606 sharedPrefs.getBoolean(PREF_ENABLE_VIDEO_CALLING_KEY, true);
607 boolean isNonTty = Settings.Secure.getInt(mContext.getContentResolver(),
608 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
609 == TelecomManager.TTY_MODE_OFF;
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800610 boolean isFeatureOn = available && enabled && isNonTty;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700611
612 log("updateVideoCallFeatureValue: available = " + available
613 + ", enabled = " + enabled
614 + ", nonTTY = " + isNonTty);
615
616 getConfigInterface().setFeatureValue(
617 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
618 TelephonyManager.NETWORK_TYPE_LTE,
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800619 isFeatureOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700620 ImsConfig.FeatureValueConstants.ON :
621 ImsConfig.FeatureValueConstants.OFF,
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -0800622 mImsConfigListener);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700623
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800624 return isFeatureOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700625 }
626
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700627 /**
628 * Update WFC config
629 * @return whether feature is On
630 * @throws ImsException
631 */
632 private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700633 boolean available = isWfcEnabledByPlatform(mContext);
634 boolean enabled = isWfcEnabledByUser(mContext);
635 int mode = getWfcMode(mContext);
636 boolean roaming = isWfcRoamingEnabledByUser(mContext);
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800637 boolean isFeatureOn = available && enabled;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700638
639 log("updateWfcFeatureAndProvisionedValues: available = " + available
640 + ", enabled = " + enabled
641 + ", mode = " + mode
642 + ", roaming = " + roaming);
643
644 getConfigInterface().setFeatureValue(
Pavel Zhamaitsiake6f99432015-09-11 10:29:30 -0700645 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
646 TelephonyManager.NETWORK_TYPE_IWLAN,
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800647 isFeatureOn ?
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700648 ImsConfig.FeatureValueConstants.ON :
649 ImsConfig.FeatureValueConstants.OFF,
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -0800650 mImsConfigListener);
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700651
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800652 if (!isFeatureOn) {
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700653 mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
654 roaming = false;
655 }
656 setWfcModeInternal(mContext, mode);
657 setWfcRoamingSettingInternal(mContext, roaming);
Pavel Zhamaitsiakdc16e452015-09-08 17:12:06 -0700658
Pavel Zhamaitsiak9510b1c2015-12-18 11:30:49 -0800659 return isFeatureOn;
Pavel Zhamaitsiak8ca52ff2015-09-04 17:08:35 -0700660 }
661
Etan Cohenabbd7882014-09-26 22:35:35 -0700662 private ImsManager(Context context, int phoneId) {
Wink Savilleef36ef62014-06-11 08:39:38 -0700663 mContext = context;
Etan Cohenabbd7882014-09-26 22:35:35 -0700664 mPhoneId = phoneId;
Wink Savilleef36ef62014-06-11 08:39:38 -0700665 createImsService(true);
666 }
667
Etan Cohenf4311122015-02-26 17:47:13 -0800668 /*
669 * Returns a flag indicating whether the IMS service is available.
670 */
671 public boolean isServiceAvailable() {
672 if (mImsService != null) {
673 return true;
674 }
675
676 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
677 if (binder != null) {
678 return true;
679 }
680
681 return false;
682 }
683
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -0800684 public void setImsConfigListener(ImsConfigListener listener) {
685 mImsConfigListener = listener;
686 }
687
Wink Savilleef36ef62014-06-11 08:39:38 -0700688 /**
689 * Opens the IMS service for making calls and/or receiving generic IMS calls.
690 * The caller may make subsquent calls through {@link #makeCall}.
691 * The IMS service will register the device to the operator's network with the credentials
692 * (from ISIM) periodically in order to receive calls from the operator's network.
693 * When the IMS service receives a new call, it will send out an intent with
694 * the provided action string.
695 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
696 *
697 * @param serviceClass a service class specified in {@link ImsServiceClass}
698 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
699 * @param incomingCallPendingIntent When an incoming call is received,
700 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
701 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
702 * as the result code and the intent to fill in the call ID; It cannot be null
703 * @param listener To listen to IMS registration events; It cannot be null
704 * @return identifier (greater than 0) for the specified service
705 * @throws NullPointerException if {@code incomingCallPendingIntent}
706 * or {@code listener} is null
707 * @throws ImsException if calling the IMS service results in an error
708 * @see #getCallId
709 * @see #getServiceId
710 */
711 public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
712 ImsConnectionStateListener listener) throws ImsException {
713 checkAndThrowExceptionIfServiceUnavailable();
714
715 if (incomingCallPendingIntent == null) {
716 throw new NullPointerException("incomingCallPendingIntent can't be null");
717 }
718
719 if (listener == null) {
720 throw new NullPointerException("listener can't be null");
721 }
722
723 int result = 0;
724
725 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700726 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
Wink Savilleef36ef62014-06-11 08:39:38 -0700727 createRegistrationListenerProxy(serviceClass, listener));
728 } catch (RemoteException e) {
729 throw new ImsException("open()", e,
730 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
731 }
732
733 if (result <= 0) {
734 // If the return value is a minus value,
735 // it means that an error occurred in the service.
736 // So, it needs to convert to the reason code specified in ImsReasonInfo.
737 throw new ImsException("open()", (result * (-1)));
738 }
739
740 return result;
741 }
742
743 /**
744 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
745 * All the resources that were allocated to the service are also released.
746 *
747 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
748 * @throws ImsException if calling the IMS service results in an error
749 */
750 public void close(int serviceId) throws ImsException {
751 checkAndThrowExceptionIfServiceUnavailable();
752
753 try {
754 mImsService.close(serviceId);
755 } catch (RemoteException e) {
756 throw new ImsException("close()", e,
757 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
758 } finally {
759 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500760 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -0700761 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -0700762 }
763 }
764
765 /**
766 * Gets the configuration interface to provision / withdraw the supplementary service settings.
767 *
768 * @param serviceId a service id which is obtained from {@link ImsManager#open}
769 * @return the Ut interface instance
770 * @throws ImsException if getting the Ut interface results in an error
771 */
772 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
773 throws ImsException {
774 // FIXME: manage the multiple Ut interfaces based on the service id
775 if (mUt == null) {
776 checkAndThrowExceptionIfServiceUnavailable();
777
778 try {
779 IImsUt iUt = mImsService.getUtInterface(serviceId);
780
781 if (iUt == null) {
782 throw new ImsException("getSupplementaryServiceConfiguration()",
783 ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
784 }
785
786 mUt = new ImsUt(iUt);
787 } catch (RemoteException e) {
788 throw new ImsException("getSupplementaryServiceConfiguration()", e,
789 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
790 }
791 }
792
793 return mUt;
794 }
795
796 /**
797 * Checks if the IMS service has successfully registered to the IMS network
798 * with the specified service & call type.
799 *
800 * @param serviceId a service id which is obtained from {@link ImsManager#open}
801 * @param serviceType a service type that is specified in {@link ImsCallProfile}
802 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
803 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
804 * @param callType a call type that is specified in {@link ImsCallProfile}
805 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
806 * {@link ImsCallProfile#CALL_TYPE_VOICE}
807 * {@link ImsCallProfile#CALL_TYPE_VT}
808 * {@link ImsCallProfile#CALL_TYPE_VS}
809 * @return true if the specified service id is connected to the IMS network;
810 * false otherwise
811 * @throws ImsException if calling the IMS service results in an error
812 */
813 public boolean isConnected(int serviceId, int serviceType, int callType)
814 throws ImsException {
815 checkAndThrowExceptionIfServiceUnavailable();
816
817 try {
818 return mImsService.isConnected(serviceId, serviceType, callType);
819 } catch (RemoteException e) {
820 throw new ImsException("isServiceConnected()", e,
821 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
822 }
823 }
824
825 /**
826 * Checks if the specified IMS service is opend.
827 *
828 * @param serviceId a service id which is obtained from {@link ImsManager#open}
829 * @return true if the specified service id is opened; false otherwise
830 * @throws ImsException if calling the IMS service results in an error
831 */
832 public boolean isOpened(int serviceId) throws ImsException {
833 checkAndThrowExceptionIfServiceUnavailable();
834
835 try {
836 return mImsService.isOpened(serviceId);
837 } catch (RemoteException e) {
838 throw new ImsException("isOpened()", e,
839 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
840 }
841 }
842
843 /**
844 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
845 *
846 * @param serviceId a service id which is obtained from {@link ImsManager#open}
847 * @param serviceType a service type that is specified in {@link ImsCallProfile}
848 * {@link ImsCallProfile#SERVICE_TYPE_NONE}
849 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
850 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
851 * @param callType a call type that is specified in {@link ImsCallProfile}
852 * {@link ImsCallProfile#CALL_TYPE_VOICE}
853 * {@link ImsCallProfile#CALL_TYPE_VT}
854 * {@link ImsCallProfile#CALL_TYPE_VT_TX}
855 * {@link ImsCallProfile#CALL_TYPE_VT_RX}
856 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
857 * {@link ImsCallProfile#CALL_TYPE_VS}
858 * {@link ImsCallProfile#CALL_TYPE_VS_TX}
859 * {@link ImsCallProfile#CALL_TYPE_VS_RX}
860 * @return a {@link ImsCallProfile} object
861 * @throws ImsException if calling the IMS service results in an error
862 */
863 public ImsCallProfile createCallProfile(int serviceId,
864 int serviceType, int callType) throws ImsException {
865 checkAndThrowExceptionIfServiceUnavailable();
866
867 try {
868 return mImsService.createCallProfile(serviceId, serviceType, callType);
869 } catch (RemoteException e) {
870 throw new ImsException("createCallProfile()", e,
871 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
872 }
873 }
874
875 /**
876 * Creates a {@link ImsCall} to make a call.
877 *
878 * @param serviceId a service id which is obtained from {@link ImsManager#open}
879 * @param profile a call profile to make the call
880 * (it contains service type, call type, media information, etc.)
881 * @param participants participants to invite the conference call
882 * @param listener listen to the call events from {@link ImsCall}
883 * @return a {@link ImsCall} object
884 * @throws ImsException if calling the IMS service results in an error
885 */
886 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
887 ImsCall.Listener listener) throws ImsException {
888 if (DBG) {
889 log("makeCall :: serviceId=" + serviceId
890 + ", profile=" + profile + ", callees=" + callees);
891 }
892
893 checkAndThrowExceptionIfServiceUnavailable();
894
895 ImsCall call = new ImsCall(mContext, profile);
896
897 call.setListener(listener);
898 ImsCallSession session = createCallSession(serviceId, profile);
899
900 if ((callees != null) && (callees.length == 1)) {
901 call.start(session, callees[0]);
902 } else {
903 call.start(session, callees);
904 }
905
906 return call;
907 }
908
909 /**
910 * Creates a {@link ImsCall} to take an incoming call.
911 *
912 * @param serviceId a service id which is obtained from {@link ImsManager#open}
913 * @param incomingCallIntent the incoming call broadcast intent
914 * @param listener to listen to the call events from {@link ImsCall}
915 * @return a {@link ImsCall} object
916 * @throws ImsException if calling the IMS service results in an error
917 */
918 public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
919 ImsCall.Listener listener) throws ImsException {
920 if (DBG) {
921 log("takeCall :: serviceId=" + serviceId
922 + ", incomingCall=" + incomingCallIntent);
923 }
924
925 checkAndThrowExceptionIfServiceUnavailable();
926
927 if (incomingCallIntent == null) {
928 throw new ImsException("Can't retrieve session with null intent",
929 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
930 }
931
932 int incomingServiceId = getServiceId(incomingCallIntent);
933
934 if (serviceId != incomingServiceId) {
935 throw new ImsException("Service id is mismatched in the incoming call intent",
936 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
937 }
938
939 String callId = getCallId(incomingCallIntent);
940
941 if (callId == null) {
942 throw new ImsException("Call ID missing in the incoming call intent",
943 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
944 }
945
946 try {
947 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
948
949 if (session == null) {
950 throw new ImsException("No pending session for the call",
951 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
952 }
953
954 ImsCall call = new ImsCall(mContext, session.getCallProfile());
955
956 call.attachSession(new ImsCallSession(session));
957 call.setListener(listener);
958
959 return call;
960 } catch (Throwable t) {
961 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
962 }
963 }
964
965 /**
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500966 * Gets the config interface to get/set service/capability parameters.
967 *
968 * @return the ImsConfig instance.
969 * @throws ImsException if getting the setting interface results in an error.
970 */
971 public ImsConfig getConfigInterface() throws ImsException {
972
973 if (mConfig == null) {
974 checkAndThrowExceptionIfServiceUnavailable();
975
976 try {
Etan Cohenabbd7882014-09-26 22:35:35 -0700977 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500978 if (config == null) {
979 throw new ImsException("getConfigInterface()",
980 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
981 }
Libin.Tang@motorola.com54953c72014-08-07 15:02:08 -0500982 mConfig = new ImsConfig(config, mContext);
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -0500983 } catch (RemoteException e) {
984 throw new ImsException("getConfigInterface()", e,
985 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
986 }
987 }
988 if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
989 return mConfig;
990 }
991
Etan Cohen82f78122014-12-15 10:10:14 -0800992 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530993 throws ImsException {
994
Etan Cohen82f78122014-12-15 10:10:14 -0800995 checkAndThrowExceptionIfServiceUnavailable();
Shriram Ganeshc403b7b2014-08-14 14:18:57 +0530996
Etan Cohen82f78122014-12-15 10:10:14 -0800997 try {
998 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
999 } catch (RemoteException e) {
1000 throw new ImsException("setTTYMode()", e,
1001 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1002 }
1003
Junda Liue7663c02015-06-23 11:16:26 -07001004 if (!getBooleanCarrierConfig(context,
1005 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
Etan Cohen82f78122014-12-15 10:10:14 -08001006 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
1007 isEnhanced4gLteModeSettingEnabledByUser(context));
1008 }
Shriram Ganeshc403b7b2014-08-14 14:18:57 +05301009 }
1010
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -05001011 /**
Junda Liue7663c02015-06-23 11:16:26 -07001012 * Get the boolean config from carrier config manager.
1013 *
1014 * @param context the context to get carrier service
1015 * @param key config key defined in CarrierConfigManager
1016 * @return boolean value of corresponding key.
1017 */
1018 private static boolean getBooleanCarrierConfig(Context context, String key) {
1019 CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1020 Context.CARRIER_CONFIG_SERVICE);
Jonathan Basseri2acea6f2015-07-01 15:00:38 -07001021 PersistableBundle b = null;
Junda Liue7663c02015-06-23 11:16:26 -07001022 if (configManager != null) {
Jonathan Basseri2acea6f2015-07-01 15:00:38 -07001023 b = configManager.getConfig();
1024 }
1025 if (b != null) {
1026 return b.getBoolean(key);
Junda Liue7663c02015-06-23 11:16:26 -07001027 } else {
1028 // Return static default defined in CarrierConfigManager.
1029 return CarrierConfigManager.getDefaultConfig().getBoolean(key);
1030 }
1031 }
1032
1033 /**
Wink Savilleef36ef62014-06-11 08:39:38 -07001034 * Gets the call ID from the specified incoming call broadcast intent.
1035 *
1036 * @param incomingCallIntent the incoming call broadcast intent
1037 * @return the call ID or null if the intent does not contain it
1038 */
1039 private static String getCallId(Intent incomingCallIntent) {
1040 if (incomingCallIntent == null) {
1041 return null;
1042 }
1043
1044 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
1045 }
1046
1047 /**
1048 * Gets the service type from the specified incoming call broadcast intent.
1049 *
1050 * @param incomingCallIntent the incoming call broadcast intent
1051 * @return the service identifier or -1 if the intent does not contain it
1052 */
1053 private static int getServiceId(Intent incomingCallIntent) {
1054 if (incomingCallIntent == null) {
1055 return (-1);
1056 }
1057
1058 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
1059 }
1060
1061 /**
1062 * Binds the IMS service only if the service is not created.
1063 */
1064 private void checkAndThrowExceptionIfServiceUnavailable()
1065 throws ImsException {
1066 if (mImsService == null) {
1067 createImsService(true);
1068
1069 if (mImsService == null) {
1070 throw new ImsException("Service is unavailable",
1071 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1072 }
1073 }
1074 }
1075
Etan Cohenabbd7882014-09-26 22:35:35 -07001076 private static String getImsServiceName(int phoneId) {
1077 // TODO: MSIM implementation needs to decide on service name as a function of phoneId
Etan Cohend7727462014-07-12 14:54:10 -07001078 return IMS_SERVICE;
1079 }
1080
Wink Savilleef36ef62014-06-11 08:39:38 -07001081 /**
1082 * Binds the IMS service to make/receive the call.
1083 */
1084 private void createImsService(boolean checkService) {
1085 if (checkService) {
Etan Cohenabbd7882014-09-26 22:35:35 -07001086 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -07001087
1088 if (binder == null) {
1089 return;
1090 }
1091 }
1092
Etan Cohenabbd7882014-09-26 22:35:35 -07001093 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
Wink Savilleef36ef62014-06-11 08:39:38 -07001094
1095 if (b != null) {
1096 try {
1097 b.linkToDeath(mDeathRecipient, 0);
1098 } catch (RemoteException e) {
1099 }
1100 }
1101
1102 mImsService = IImsService.Stub.asInterface(b);
1103 }
1104
1105 /**
1106 * Creates a {@link ImsCallSession} with the specified call profile.
1107 * Use other methods, if applicable, instead of interacting with
1108 * {@link ImsCallSession} directly.
1109 *
1110 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1111 * @param profile a call profile to make the call
1112 */
1113 private ImsCallSession createCallSession(int serviceId,
1114 ImsCallProfile profile) throws ImsException {
1115 try {
1116 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
1117 } catch (RemoteException e) {
1118 return null;
1119 }
1120 }
1121
1122 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
1123 ImsConnectionStateListener listener) {
1124 ImsRegistrationListenerProxy proxy =
1125 new ImsRegistrationListenerProxy(serviceClass, listener);
1126 return proxy;
1127 }
1128
Etan Cohena00c9192014-12-23 15:02:29 -08001129 private static void log(String s) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001130 Rlog.d(TAG, s);
1131 }
1132
Etan Cohena00c9192014-12-23 15:02:29 -08001133 private static void loge(String s) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001134 Rlog.e(TAG, s);
1135 }
1136
Etan Cohena00c9192014-12-23 15:02:29 -08001137 private static void loge(String s, Throwable t) {
Wink Savilleef36ef62014-06-11 08:39:38 -07001138 Rlog.e(TAG, s, t);
1139 }
1140
1141 /**
ram7da5a112014-07-16 20:59:27 +05301142 * Used for turning on IMS.if its off already
1143 */
Etan Cohen82f78122014-12-15 10:10:14 -08001144 private void turnOnIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -07001145 checkAndThrowExceptionIfServiceUnavailable();
1146
ram7da5a112014-07-16 20:59:27 +05301147 try {
Etan Cohenabbd7882014-09-26 22:35:35 -07001148 mImsService.turnOnIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +05301149 } catch (RemoteException e) {
1150 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1151 }
1152 }
1153
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001154 private boolean isImsTurnOffAllowed() {
1155 return getBooleanCarrierConfig(mContext,
1156 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
1157 && (!isWfcEnabledByPlatform(mContext)
1158 || !isWfcEnabledByUser(mContext));
1159 }
1160
Etan Cohen82f78122014-12-15 10:10:14 -08001161 private void setAdvanced4GMode(boolean turnOn) throws ImsException {
Etan Cohencfc784d2014-08-07 18:40:31 -07001162 checkAndThrowExceptionIfServiceUnavailable();
1163
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001164 try {
1165 ImsConfig config = getConfigInterface();
1166 if (config != null && (turnOn || !isImsTurnOffAllowed())) {
1167 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -08001168 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
1169
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001170 if (isVtEnabledByPlatform(mContext)) {
1171 // TODO: once VT is available on platform:
1172 // - replace the '1' with the current user configuration of VT.
1173 // - separate exception checks for setFeatureValue() failures for VoLTE and VT.
1174 // I.e. if VoLTE fails still try to configure VT.
1175 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
Pavel Zhamaitsiakf4b90322016-01-26 14:34:09 -08001176 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001177 }
Etan Cohenb651fa52014-10-22 10:51:29 -07001178 }
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001179 } catch (ImsException e) {
1180 log("setAdvanced4GMode() : " + e);
Etan Cohencfc784d2014-08-07 18:40:31 -07001181 }
Etan Cohencfc784d2014-08-07 18:40:31 -07001182 if (turnOn) {
1183 turnOnIms();
Omkar Kolangade75f3ca32014-10-24 11:10:52 -07001184 } else if (isImsTurnOffAllowed()) {
Etan Cohencfc784d2014-08-07 18:40:31 -07001185 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms");
1186 turnOffIms();
1187 }
1188 }
1189
ram7da5a112014-07-16 20:59:27 +05301190 /**
1191 * Used for turning off IMS completely in order to make the device CSFB'ed.
1192 * Once turned off, all calls will be over CS.
1193 */
Etan Cohen82f78122014-12-15 10:10:14 -08001194 private void turnOffIms() throws ImsException {
Etan Cohen0c9c09e2014-07-25 11:09:12 -07001195 checkAndThrowExceptionIfServiceUnavailable();
1196
ram7da5a112014-07-16 20:59:27 +05301197 try {
Etan Cohenabbd7882014-09-26 22:35:35 -07001198 mImsService.turnOffIms(mPhoneId);
ram7da5a112014-07-16 20:59:27 +05301199 } catch (RemoteException e) {
1200 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1201 }
1202 }
1203
1204 /**
Wink Savilleef36ef62014-06-11 08:39:38 -07001205 * Death recipient class for monitoring IMS service.
1206 */
1207 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
1208 @Override
1209 public void binderDied() {
1210 mImsService = null;
1211 mUt = null;
Libin.Tang@motorola.com076c55d2014-06-23 19:46:36 -05001212 mConfig = null;
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -07001213 mEcbm = null;
Wink Savilleef36ef62014-06-11 08:39:38 -07001214
1215 if (mContext != null) {
Etan Cohend7727462014-07-12 14:54:10 -07001216 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
Etan Cohenabbd7882014-09-26 22:35:35 -07001217 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
Etan Cohend7727462014-07-12 14:54:10 -07001218 mContext.sendBroadcast(new Intent(intent));
Wink Savilleef36ef62014-06-11 08:39:38 -07001219 }
1220 }
1221 }
1222
1223 /**
1224 * Adapter class for {@link IImsRegistrationListener}.
1225 */
1226 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
1227 private int mServiceClass;
1228 private ImsConnectionStateListener mListener;
1229
1230 public ImsRegistrationListenerProxy(int serviceClass,
1231 ImsConnectionStateListener listener) {
1232 mServiceClass = serviceClass;
1233 mListener = listener;
1234 }
1235
1236 public boolean isSameProxy(int serviceClass) {
1237 return (mServiceClass == serviceClass);
1238 }
1239
Omkar Kolangadea7ced642015-05-04 17:55:13 -07001240 @Deprecated
Wink Savilleef36ef62014-06-11 08:39:38 -07001241 public void registrationConnected() {
1242 if (DBG) {
1243 log("registrationConnected ::");
1244 }
1245
1246 if (mListener != null) {
1247 mListener.onImsConnected();
1248 }
1249 }
1250
Omkar Kolangadea7ced642015-05-04 17:55:13 -07001251 @Deprecated
Rekha Kumar14631742015-02-04 10:47:00 -08001252 public void registrationProgressing() {
Wink Savilleef36ef62014-06-11 08:39:38 -07001253 if (DBG) {
Rekha Kumar14631742015-02-04 10:47:00 -08001254 log("registrationProgressing ::");
Wink Savilleef36ef62014-06-11 08:39:38 -07001255 }
1256
1257 if (mListener != null) {
Rekha Kumar14631742015-02-04 10:47:00 -08001258 mListener.onImsProgressing();
1259 }
1260 }
1261
1262 @Override
Omkar Kolangadea7ced642015-05-04 17:55:13 -07001263 public void registrationConnectedWithRadioTech(int imsRadioTech) {
1264 // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
1265 // values in ServiceState.java.
1266 if (DBG) {
1267 log("registrationConnectedWithRadioTech :: imsRadioTech=" + imsRadioTech);
1268 }
1269
1270 if (mListener != null) {
1271 mListener.onImsConnected();
1272 }
1273 }
1274
1275 @Override
1276 public void registrationProgressingWithRadioTech(int imsRadioTech) {
1277 // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
1278 // values in ServiceState.java.
1279 if (DBG) {
1280 log("registrationProgressingWithRadioTech :: imsRadioTech=" + imsRadioTech);
1281 }
1282
1283 if (mListener != null) {
1284 mListener.onImsProgressing();
1285 }
1286 }
1287
1288 @Override
Rekha Kumar14631742015-02-04 10:47:00 -08001289 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
1290 if (DBG) {
1291 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
1292 }
1293
1294 if (mListener != null) {
1295 mListener.onImsDisconnected(imsReasonInfo);
Wink Savilleef36ef62014-06-11 08:39:38 -07001296 }
1297 }
1298
1299 @Override
1300 public void registrationResumed() {
1301 if (DBG) {
1302 log("registrationResumed ::");
1303 }
1304
1305 if (mListener != null) {
1306 mListener.onImsResumed();
1307 }
1308 }
1309
1310 @Override
1311 public void registrationSuspended() {
1312 if (DBG) {
1313 log("registrationSuspended ::");
1314 }
1315
1316 if (mListener != null) {
1317 mListener.onImsSuspended();
1318 }
1319 }
1320
1321 @Override
1322 public void registrationServiceCapabilityChanged(int serviceClass, int event) {
1323 log("registrationServiceCapabilityChanged :: serviceClass=" +
1324 serviceClass + ", event=" + event);
1325
1326 if (mListener != null) {
1327 mListener.onImsConnected();
1328 }
1329 }
ram7da5a112014-07-16 20:59:27 +05301330
1331 @Override
1332 public void registrationFeatureCapabilityChanged(int serviceClass,
1333 int[] enabledFeatures, int[] disabledFeatures) {
1334 log("registrationFeatureCapabilityChanged :: serviceClass=" +
1335 serviceClass);
Libin.Tang@motorola.come2296782014-08-19 14:20:01 -05001336 if (mListener != null) {
1337 mListener.onFeatureCapabilityChanged(serviceClass,
1338 enabledFeatures, disabledFeatures);
1339 }
ram7da5a112014-07-16 20:59:27 +05301340 }
1341
Shriram Ganeshd3adfad2015-05-31 10:06:15 -07001342 @Override
1343 public void voiceMessageCountUpdate(int count) {
1344 log("voiceMessageCountUpdate :: count=" + count);
1345
1346 if (mListener != null) {
1347 mListener.onVoiceMessageCountChanged(count);
1348 }
1349 }
1350
Pavel Zhamaitsiak4de9cbb2016-02-11 17:21:05 -08001351 @Override
1352 public void registrationAssociatedUriChanged(Uri[] uris) {
1353 if (DBG) log("registrationAssociatedUriChanged ::");
1354
1355 if (mListener != null) {
1356 mListener.registrationAssociatedUriChanged(uris);
1357 }
1358 }
Wink Savilleef36ef62014-06-11 08:39:38 -07001359 }
Uma Maheswari Ramalingam4f2161d2014-07-31 16:01:47 -07001360 /**
1361 * Gets the ECBM interface to request ECBM exit.
1362 *
1363 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1364 * @return the ECBM interface instance
1365 * @throws ImsException if getting the ECBM interface results in an error
1366 */
1367 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
1368 if (mEcbm == null) {
1369 checkAndThrowExceptionIfServiceUnavailable();
1370
1371 try {
1372 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
1373
1374 if (iEcbm == null) {
1375 throw new ImsException("getEcbmInterface()",
1376 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
1377 }
1378 mEcbm = new ImsEcbm(iEcbm);
1379 } catch (RemoteException e) {
1380 throw new ImsException("getEcbmInterface()", e,
1381 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1382 }
1383 }
1384 return mEcbm;
1385 }
Pavel Zhamaitsiak8c065f52015-11-10 14:36:44 -08001386
1387 /**
1388 * Resets ImsManager settings back to factory defaults.
1389 *
1390 * @hide
1391 */
1392 public static void factoryReset(Context context) {
1393 // Set VoLTE to default
1394 android.provider.Settings.Global.putInt(context.getContentResolver(),
1395 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
1396 ImsConfig.FeatureValueConstants.ON);
1397
1398 // Set VoWiFi to default
1399 android.provider.Settings.Global.putInt(context.getContentResolver(),
1400 android.provider.Settings.Global.WFC_IMS_ENABLED,
1401 ImsConfig.FeatureValueConstants.OFF);
1402
1403 // Set VoWiFi mode to default
1404 android.provider.Settings.Global.putInt(context.getContentResolver(),
1405 android.provider.Settings.Global.WFC_IMS_MODE,
1406 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED);
1407
1408 // Set VoWiFi roaming to default
1409 android.provider.Settings.Global.putInt(context.getContentResolver(),
1410 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1411 ImsConfig.FeatureValueConstants.OFF);
1412
1413 // Set VT to default
1414 SharedPreferences sharedPrefs =
1415 PreferenceManager.getDefaultSharedPreferences(context);
1416 SharedPreferences.Editor editor = sharedPrefs.edit();
1417 editor.putBoolean(PREF_ENABLE_VIDEO_CALLING_KEY, true);
1418 editor.commit();
1419
1420 // Push settings to ImsConfig
1421 ImsManager.updateImsServiceConfig(context,
1422 SubscriptionManager.getDefaultVoicePhoneId(), true);
1423 }
Jack Yu2f102bd2015-12-28 15:31:48 -08001424
1425 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1426 pw.println("ImsManager:");
1427 pw.println(" mPhoneId = " + mPhoneId);
1428 pw.println(" mConfigUpdated = " + mConfigUpdated);
1429 pw.println(" mImsService = " + mImsService);
1430
1431 pw.println(" isGbaValid = " + isGbaValid(mContext));
1432 pw.println(" isImsTurnOffAllowed = " + isImsTurnOffAllowed());
1433 pw.println(" isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabled(mContext));
1434
1435 pw.println(" isVolteEnabledByPlatform = " + isVolteEnabledByPlatform(mContext));
1436 pw.println(" isVolteProvisionedOnDevice = " + isVolteProvisionedOnDevice(mContext));
1437 pw.println(" isEnhanced4gLteModeSettingEnabledByUser = " +
1438 isEnhanced4gLteModeSettingEnabledByUser(mContext));
1439 pw.println(" isVtEnabledByPlatform = " + isVtEnabledByPlatform(mContext));
1440 pw.println(" isVtEnabledByUser = " + isVtEnabledByUser(mContext));
1441
1442 pw.println(" isWfcEnabledByPlatform = " + isWfcEnabledByPlatform(mContext));
1443 pw.println(" isWfcEnabledByUser = " + isWfcEnabledByUser(mContext));
1444 pw.println(" getWfcMode = " + getWfcMode(mContext));
1445 pw.println(" isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUser(mContext));
1446
1447 pw.flush();
1448 }
Wink Savilleef36ef62014-06-11 08:39:38 -07001449}