2 * Copyright (c) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.ims;
19 import android.app.PendingIntent;
20 import android.app.QueuedWork;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.SharedPreferences;
24 import android.os.IBinder;
25 import android.os.Message;
26 import android.os.PersistableBundle;
27 import android.os.RemoteException;
28 import android.os.ServiceManager;
29 import android.os.SystemProperties;
30 import android.preference.PreferenceManager;
31 import android.provider.Settings;
32 import android.telecom.TelecomManager;
33 import android.telephony.CarrierConfigManager;
34 import android.telephony.Rlog;
35 import android.telephony.SubscriptionManager;
36 import android.telephony.TelephonyManager;
38 import com.android.ims.internal.IImsCallSession;
39 import com.android.ims.internal.IImsEcbm;
40 import com.android.ims.internal.IImsEcbmListener;
41 import com.android.ims.internal.IImsRegistrationListener;
42 import com.android.ims.internal.IImsService;
43 import com.android.ims.internal.IImsUt;
44 import com.android.ims.internal.ImsCallSession;
45 import com.android.ims.internal.IImsConfig;
47 import java.util.HashMap;
50 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
51 * the operator's IMS network. This class is the starting point for any IMS actions.
52 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
53 * <p>The APIs in this class allows you to:</p>
57 public class ImsManager {
60 * Debug flag to override configuration flag
62 public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
63 public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
64 public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
65 public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
66 public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
67 public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
70 * For accessing the IMS related service.
74 private static final String IMS_SERVICE = "ims";
77 * The result code to be sent back with the incoming call {@link PendingIntent}.
78 * @see #open(PendingIntent, ImsConnectionStateListener)
80 public static final int INCOMING_CALL_RESULT_CODE = 101;
83 * Key to retrieve the call ID from an incoming call intent.
84 * @see #open(PendingIntent, ImsConnectionStateListener)
86 public static final String EXTRA_CALL_ID = "android:imsCallID";
89 * Action to broadcast when ImsService is up.
93 public static final String ACTION_IMS_SERVICE_UP =
94 "com.android.ims.IMS_SERVICE_UP";
97 * Action to broadcast when ImsService is down.
101 public static final String ACTION_IMS_SERVICE_DOWN =
102 "com.android.ims.IMS_SERVICE_DOWN";
105 * Action to broadcast when ImsService registration fails.
109 public static final String ACTION_IMS_REGISTRATION_ERROR =
110 "com.android.ims.REGISTRATION_ERROR";
113 * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
114 * A long value; the phone ID corresponding to the IMS service coming up or down.
118 public static final String EXTRA_PHONE_ID = "android:phone_id";
121 * Action for the incoming call intent for the Phone app.
125 public static final String ACTION_IMS_INCOMING_CALL =
126 "com.android.ims.IMS_INCOMING_CALL";
129 * Part of the ACTION_IMS_INCOMING_CALL intents.
130 * An integer value; service identifier obtained from {@link ImsManager#open}.
134 public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
137 * Part of the ACTION_IMS_INCOMING_CALL intents.
138 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
139 * The value "true" indicates that the incoming call is for USSD.
143 public static final String EXTRA_USSD = "android:ussd";
146 * Part of the ACTION_IMS_INCOMING_CALL intents.
147 * A boolean value; Flag to indicate whether the call is an unknown
148 * dialing call. Such calls are originated by sending commands (like
149 * AT commands) directly to modem without Android involvement.
150 * Even though they are not incoming calls, they are propagated
151 * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
155 public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
157 private static final String TAG = "ImsManager";
158 private static final boolean DBG = true;
160 private static HashMap<Integer, ImsManager> sImsManagerInstances =
161 new HashMap<Integer, ImsManager>();
163 private Context mContext;
164 private int mPhoneId;
165 private IImsService mImsService = null;
166 private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
167 // Ut interface for the supplementary service configuration
168 private ImsUt mUt = null;
169 // Interface to get/set ims config items
170 private ImsConfig mConfig = null;
171 private boolean mConfigUpdated = false;
172 private static final String PREF_ENABLE_VIDEO_CALLING_KEY = "enable_video_calling";
175 private ImsEcbm mEcbm = null;
178 * Gets a manager instance.
180 * @param context application context for creating the manager object
181 * @param phoneId the phone ID for the IMS Service
182 * @return the manager instance corresponding to the phoneId
184 public static ImsManager getInstance(Context context, int phoneId) {
185 synchronized (sImsManagerInstances) {
186 if (sImsManagerInstances.containsKey(phoneId))
187 return sImsManagerInstances.get(phoneId);
189 ImsManager mgr = new ImsManager(context, phoneId);
190 sImsManagerInstances.put(phoneId, mgr);
197 * Returns the user configuration of Enhanced 4G LTE Mode setting
199 public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
200 int enabled = android.provider.Settings.Global.getInt(
201 context.getContentResolver(),
202 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
203 return (enabled == 1) ? true : false;
207 * Change persistent Enhanced 4G LTE Mode setting
209 public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
210 int value = enabled ? 1 : 0;
211 android.provider.Settings.Global.putInt(
212 context.getContentResolver(),
213 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
215 if (isNonTtyOrTtyOnVolteEnabled(context)) {
216 ImsManager imsManager = ImsManager.getInstance(context,
217 SubscriptionManager.getDefaultVoicePhoneId());
218 if (imsManager != null) {
220 imsManager.setAdvanced4GMode(enabled);
221 } catch (ImsException ie) {
229 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
232 public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
233 if (getBooleanCarrierConfig(context,
234 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
238 return Settings.Secure.getInt(context.getContentResolver(),
239 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
240 == TelecomManager.TTY_MODE_OFF;
244 * Returns a platform configuration for VoLTE which may override the user setting.
246 public static boolean isVolteEnabledByPlatform(Context context) {
247 if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
248 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
252 return context.getResources().getBoolean(
253 com.android.internal.R.bool.config_device_volte_available)
254 && getBooleanCarrierConfig(context,
255 CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
256 && isGbaValid(context);
260 * Indicates whether VoLTE is provisioned on device
262 public static boolean isVolteProvisionedOnDevice(Context context) {
263 boolean isProvisioned = true;
264 if (getBooleanCarrierConfig(context,
265 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
266 isProvisioned = false; // disable on any error
267 ImsManager mgr = ImsManager.getInstance(context,
268 SubscriptionManager.getDefaultVoicePhoneId());
271 ImsConfig config = mgr.getConfigInterface();
272 if (config != null) {
273 isProvisioned = config.getVolteProvisioned();
275 } catch (ImsException ie) {
281 return isProvisioned;
285 * Returns a platform configuration for VT which may override the user setting.
287 * Note: VT presumes that VoLTE is enabled (these are configuration settings
288 * which must be done correctly).
290 public static boolean isVtEnabledByPlatform(Context context) {
291 if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
292 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
297 context.getResources().getBoolean(
298 com.android.internal.R.bool.config_device_vt_available) &&
299 getBooleanCarrierConfig(context,
300 CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
305 * Returns the user configuration of WFC setting
307 public static boolean isWfcEnabledByUser(Context context) {
308 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
309 android.provider.Settings.Global.WFC_IMS_ENABLED,
310 ImsConfig.FeatureValueConstants.OFF);
311 return (enabled == 1) ? true : false;
315 * Change persistent WFC enabled setting
317 public static void setWfcSetting(Context context, boolean enabled) {
318 int value = enabled ? 1 : 0;
319 android.provider.Settings.Global.putInt(context.getContentResolver(),
320 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
322 ImsManager imsManager = ImsManager.getInstance(context,
323 SubscriptionManager.getDefaultVoicePhoneId());
324 if (imsManager != null) {
326 ImsConfig config = imsManager.getConfigInterface();
327 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
328 TelephonyManager.NETWORK_TYPE_IWLAN,
329 enabled ? ImsConfig.FeatureValueConstants.ON
330 : ImsConfig.FeatureValueConstants.OFF, null);
333 imsManager.turnOnIms();
334 } else if (getBooleanCarrierConfig(context,
335 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
336 && (!isVolteEnabledByPlatform(context)
337 || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
338 log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
339 imsManager.turnOffIms();
342 // Force IMS to register over LTE when turning off WFC
343 setWfcModeInternal(context, enabled
344 ? getWfcMode(context)
345 : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
346 } catch (ImsException e) {
347 loge("setWfcSetting(): " + e);
353 * Returns the user configuration of WFC modem setting
355 public static int getWfcMode(Context context) {
356 int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
357 android.provider.Settings.Global.WFC_IMS_MODE,
358 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED);
359 if (DBG) log("getWfcMode - setting=" + setting);
364 * Returns the user configuration of WFC modem setting
366 public static void setWfcMode(Context context, int wfcMode) {
367 if (DBG) log("setWfcMode - setting=" + wfcMode);
368 android.provider.Settings.Global.putInt(context.getContentResolver(),
369 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
371 setWfcModeInternal(context, wfcMode);
374 private static void setWfcModeInternal(Context context, int wfcMode) {
375 final ImsManager imsManager = ImsManager.getInstance(context,
376 SubscriptionManager.getDefaultVoicePhoneId());
377 if (imsManager != null) {
378 final int value = wfcMode;
379 QueuedWork.singleThreadExecutor().submit(new Runnable() {
382 imsManager.getConfigInterface().setProvisionedValue(
383 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
385 } catch (ImsException e) {
394 * Returns the user configuration of WFC roaming setting
396 public static boolean isWfcRoamingEnabledByUser(Context context) {
397 int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
398 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
399 ImsConfig.FeatureValueConstants.OFF);
400 return (enabled == 1) ? true : false;
404 * Change persistent WFC roaming enabled setting
406 public static void setWfcRoamingSetting(Context context, boolean enabled) {
407 android.provider.Settings.Global.putInt(context.getContentResolver(),
408 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
410 ? ImsConfig.FeatureValueConstants.ON
411 : ImsConfig.FeatureValueConstants.OFF);
413 setWfcRoamingSettingInternal(context, enabled);
416 private static void setWfcRoamingSettingInternal(Context context, boolean enabled) {
417 final ImsManager imsManager = ImsManager.getInstance(context,
418 SubscriptionManager.getDefaultVoicePhoneId());
419 if (imsManager != null) {
420 final int value = enabled
421 ? ImsConfig.FeatureValueConstants.ON
422 : ImsConfig.FeatureValueConstants.OFF;
423 QueuedWork.singleThreadExecutor().submit(new Runnable() {
426 imsManager.getConfigInterface().setProvisionedValue(
427 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
429 } catch (ImsException e) {
438 * Returns a platform configuration for WFC which may override the user
439 * setting. Note: WFC presumes that VoLTE is enabled (these are
440 * configuration settings which must be done correctly).
442 public static boolean isWfcEnabledByPlatform(Context context) {
443 if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
444 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
449 context.getResources().getBoolean(
450 com.android.internal.R.bool.config_device_wfc_ims_available) &&
451 getBooleanCarrierConfig(context,
452 CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
457 * If carrier requires that IMS is only available if GBA capable SIM is used,
458 * then this function checks GBA bit in EF IST.
460 * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
462 private static boolean isGbaValid(Context context) {
463 if (getBooleanCarrierConfig(context,
464 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
465 final TelephonyManager telephonyManager = TelephonyManager.getDefault();
466 String efIst = telephonyManager.getIsimIst();
471 boolean result = efIst != null && efIst.length() > 1 &&
472 (0x02 & (byte)efIst.charAt(1)) != 0;
473 if (DBG) log("GBA capable=" + result + ", ISF=" + efIst);
480 * Sync carrier config and user settings with ImsConfig.
482 * @param context for the manager object
483 * @param phoneId phone id
484 * @param force update
486 public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
487 final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
488 if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
490 boolean turnOn = imsManager.updateVolteFeatureValue();
491 turnOn |= imsManager.updateVideoCallFeatureValue();
492 turnOn |= imsManager.updateWfcFeatureAndProvisionedValues();
495 imsManager.turnOnIms();
496 } else if (getBooleanCarrierConfig(context,
497 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)) {
498 imsManager.turnOffIms();
501 imsManager.mConfigUpdated = true;
502 } catch (ImsException e) {
503 loge("updateImsServiceConfig: " + e);
504 imsManager.mConfigUpdated = false;
510 * Update VoLTE config
511 * @return whether feature is On
512 * @throws ImsException
514 private boolean updateVolteFeatureValue() throws ImsException {
515 boolean available = isVolteEnabledByPlatform(mContext);
516 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext);
517 boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
518 boolean turnOn = available && enabled && isNonTty;
520 log("updateVolteFeatureValue: available = " + available
521 + ", enabled = " + enabled
522 + ", nonTTY = " + isNonTty);
524 getConfigInterface().setFeatureValue(
525 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
526 TelephonyManager.NETWORK_TYPE_LTE,
528 ImsConfig.FeatureValueConstants.ON :
529 ImsConfig.FeatureValueConstants.OFF,
537 * @return whether feature is On
538 * @throws ImsException
540 private boolean updateVideoCallFeatureValue() throws ImsException {
541 boolean available = isVtEnabledByPlatform(mContext);
542 SharedPreferences sharedPrefs =
543 PreferenceManager.getDefaultSharedPreferences(mContext);
544 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
545 sharedPrefs.getBoolean(PREF_ENABLE_VIDEO_CALLING_KEY, true);
546 boolean isNonTty = Settings.Secure.getInt(mContext.getContentResolver(),
547 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
548 == TelecomManager.TTY_MODE_OFF;
549 boolean turnOn = available && enabled && isNonTty;
551 log("updateVideoCallFeatureValue: available = " + available
552 + ", enabled = " + enabled
553 + ", nonTTY = " + isNonTty);
555 getConfigInterface().setFeatureValue(
556 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
557 TelephonyManager.NETWORK_TYPE_LTE,
559 ImsConfig.FeatureValueConstants.ON :
560 ImsConfig.FeatureValueConstants.OFF,
568 * @return whether feature is On
569 * @throws ImsException
571 private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
572 boolean available = isWfcEnabledByPlatform(mContext);
573 boolean enabled = isWfcEnabledByUser(mContext);
574 int mode = getWfcMode(mContext);
575 boolean roaming = isWfcRoamingEnabledByUser(mContext);
576 boolean turnOn = available && enabled;
578 log("updateWfcFeatureAndProvisionedValues: available = " + available
579 + ", enabled = " + enabled
581 + ", roaming = " + roaming);
583 getConfigInterface().setFeatureValue(
584 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
585 TelephonyManager.NETWORK_TYPE_IWLAN,
587 ImsConfig.FeatureValueConstants.ON :
588 ImsConfig.FeatureValueConstants.OFF,
592 mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
595 setWfcModeInternal(mContext, mode);
596 setWfcRoamingSettingInternal(mContext, roaming);
601 private ImsManager(Context context, int phoneId) {
604 createImsService(true);
608 * Returns a flag indicating whether the IMS service is available.
610 public boolean isServiceAvailable() {
611 if (mImsService != null) {
615 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
616 if (binder != null) {
624 * Opens the IMS service for making calls and/or receiving generic IMS calls.
625 * The caller may make subsquent calls through {@link #makeCall}.
626 * The IMS service will register the device to the operator's network with the credentials
627 * (from ISIM) periodically in order to receive calls from the operator's network.
628 * When the IMS service receives a new call, it will send out an intent with
629 * the provided action string.
630 * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
632 * @param serviceClass a service class specified in {@link ImsServiceClass}
633 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
634 * @param incomingCallPendingIntent When an incoming call is received,
635 * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
636 * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
637 * as the result code and the intent to fill in the call ID; It cannot be null
638 * @param listener To listen to IMS registration events; It cannot be null
639 * @return identifier (greater than 0) for the specified service
640 * @throws NullPointerException if {@code incomingCallPendingIntent}
641 * or {@code listener} is null
642 * @throws ImsException if calling the IMS service results in an error
646 public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
647 ImsConnectionStateListener listener) throws ImsException {
648 checkAndThrowExceptionIfServiceUnavailable();
650 if (incomingCallPendingIntent == null) {
651 throw new NullPointerException("incomingCallPendingIntent can't be null");
654 if (listener == null) {
655 throw new NullPointerException("listener can't be null");
661 result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
662 createRegistrationListenerProxy(serviceClass, listener));
663 } catch (RemoteException e) {
664 throw new ImsException("open()", e,
665 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
669 // If the return value is a minus value,
670 // it means that an error occurred in the service.
671 // So, it needs to convert to the reason code specified in ImsReasonInfo.
672 throw new ImsException("open()", (result * (-1)));
679 * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
680 * All the resources that were allocated to the service are also released.
682 * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
683 * @throws ImsException if calling the IMS service results in an error
685 public void close(int serviceId) throws ImsException {
686 checkAndThrowExceptionIfServiceUnavailable();
689 mImsService.close(serviceId);
690 } catch (RemoteException e) {
691 throw new ImsException("close()", e,
692 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
701 * Gets the configuration interface to provision / withdraw the supplementary service settings.
703 * @param serviceId a service id which is obtained from {@link ImsManager#open}
704 * @return the Ut interface instance
705 * @throws ImsException if getting the Ut interface results in an error
707 public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
708 throws ImsException {
709 // FIXME: manage the multiple Ut interfaces based on the service id
711 checkAndThrowExceptionIfServiceUnavailable();
714 IImsUt iUt = mImsService.getUtInterface(serviceId);
717 throw new ImsException("getSupplementaryServiceConfiguration()",
718 ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
721 mUt = new ImsUt(iUt);
722 } catch (RemoteException e) {
723 throw new ImsException("getSupplementaryServiceConfiguration()", e,
724 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
732 * Checks if the IMS service has successfully registered to the IMS network
733 * with the specified service & call type.
735 * @param serviceId a service id which is obtained from {@link ImsManager#open}
736 * @param serviceType a service type that is specified in {@link ImsCallProfile}
737 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
738 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
739 * @param callType a call type that is specified in {@link ImsCallProfile}
740 * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
741 * {@link ImsCallProfile#CALL_TYPE_VOICE}
742 * {@link ImsCallProfile#CALL_TYPE_VT}
743 * {@link ImsCallProfile#CALL_TYPE_VS}
744 * @return true if the specified service id is connected to the IMS network;
746 * @throws ImsException if calling the IMS service results in an error
748 public boolean isConnected(int serviceId, int serviceType, int callType)
749 throws ImsException {
750 checkAndThrowExceptionIfServiceUnavailable();
753 return mImsService.isConnected(serviceId, serviceType, callType);
754 } catch (RemoteException e) {
755 throw new ImsException("isServiceConnected()", e,
756 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
761 * Checks if the specified IMS service is opend.
763 * @param serviceId a service id which is obtained from {@link ImsManager#open}
764 * @return true if the specified service id is opened; false otherwise
765 * @throws ImsException if calling the IMS service results in an error
767 public boolean isOpened(int serviceId) throws ImsException {
768 checkAndThrowExceptionIfServiceUnavailable();
771 return mImsService.isOpened(serviceId);
772 } catch (RemoteException e) {
773 throw new ImsException("isOpened()", e,
774 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
779 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
781 * @param serviceId a service id which is obtained from {@link ImsManager#open}
782 * @param serviceType a service type that is specified in {@link ImsCallProfile}
783 * {@link ImsCallProfile#SERVICE_TYPE_NONE}
784 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
785 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
786 * @param callType a call type that is specified in {@link ImsCallProfile}
787 * {@link ImsCallProfile#CALL_TYPE_VOICE}
788 * {@link ImsCallProfile#CALL_TYPE_VT}
789 * {@link ImsCallProfile#CALL_TYPE_VT_TX}
790 * {@link ImsCallProfile#CALL_TYPE_VT_RX}
791 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
792 * {@link ImsCallProfile#CALL_TYPE_VS}
793 * {@link ImsCallProfile#CALL_TYPE_VS_TX}
794 * {@link ImsCallProfile#CALL_TYPE_VS_RX}
795 * @return a {@link ImsCallProfile} object
796 * @throws ImsException if calling the IMS service results in an error
798 public ImsCallProfile createCallProfile(int serviceId,
799 int serviceType, int callType) throws ImsException {
800 checkAndThrowExceptionIfServiceUnavailable();
803 return mImsService.createCallProfile(serviceId, serviceType, callType);
804 } catch (RemoteException e) {
805 throw new ImsException("createCallProfile()", e,
806 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
811 * Creates a {@link ImsCall} to make a call.
813 * @param serviceId a service id which is obtained from {@link ImsManager#open}
814 * @param profile a call profile to make the call
815 * (it contains service type, call type, media information, etc.)
816 * @param participants participants to invite the conference call
817 * @param listener listen to the call events from {@link ImsCall}
818 * @return a {@link ImsCall} object
819 * @throws ImsException if calling the IMS service results in an error
821 public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
822 ImsCall.Listener listener) throws ImsException {
824 log("makeCall :: serviceId=" + serviceId
825 + ", profile=" + profile + ", callees=" + callees);
828 checkAndThrowExceptionIfServiceUnavailable();
830 ImsCall call = new ImsCall(mContext, profile);
832 call.setListener(listener);
833 ImsCallSession session = createCallSession(serviceId, profile);
835 if ((callees != null) && (callees.length == 1)) {
836 call.start(session, callees[0]);
838 call.start(session, callees);
845 * Creates a {@link ImsCall} to take an incoming call.
847 * @param serviceId a service id which is obtained from {@link ImsManager#open}
848 * @param incomingCallIntent the incoming call broadcast intent
849 * @param listener to listen to the call events from {@link ImsCall}
850 * @return a {@link ImsCall} object
851 * @throws ImsException if calling the IMS service results in an error
853 public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
854 ImsCall.Listener listener) throws ImsException {
856 log("takeCall :: serviceId=" + serviceId
857 + ", incomingCall=" + incomingCallIntent);
860 checkAndThrowExceptionIfServiceUnavailable();
862 if (incomingCallIntent == null) {
863 throw new ImsException("Can't retrieve session with null intent",
864 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
867 int incomingServiceId = getServiceId(incomingCallIntent);
869 if (serviceId != incomingServiceId) {
870 throw new ImsException("Service id is mismatched in the incoming call intent",
871 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
874 String callId = getCallId(incomingCallIntent);
876 if (callId == null) {
877 throw new ImsException("Call ID missing in the incoming call intent",
878 ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
882 IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
884 if (session == null) {
885 throw new ImsException("No pending session for the call",
886 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
889 ImsCall call = new ImsCall(mContext, session.getCallProfile());
891 call.attachSession(new ImsCallSession(session));
892 call.setListener(listener);
895 } catch (Throwable t) {
896 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
901 * Gets the config interface to get/set service/capability parameters.
903 * @return the ImsConfig instance.
904 * @throws ImsException if getting the setting interface results in an error.
906 public ImsConfig getConfigInterface() throws ImsException {
908 if (mConfig == null) {
909 checkAndThrowExceptionIfServiceUnavailable();
912 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
913 if (config == null) {
914 throw new ImsException("getConfigInterface()",
915 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
917 mConfig = new ImsConfig(config, mContext);
918 } catch (RemoteException e) {
919 throw new ImsException("getConfigInterface()", e,
920 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
923 if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
927 public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
928 throws ImsException {
930 checkAndThrowExceptionIfServiceUnavailable();
933 mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
934 } catch (RemoteException e) {
935 throw new ImsException("setTTYMode()", e,
936 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
939 if (!getBooleanCarrierConfig(context,
940 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
941 setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
942 isEnhanced4gLteModeSettingEnabledByUser(context));
947 * Get the boolean config from carrier config manager.
949 * @param context the context to get carrier service
950 * @param key config key defined in CarrierConfigManager
951 * @return boolean value of corresponding key.
953 private static boolean getBooleanCarrierConfig(Context context, String key) {
954 CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
955 Context.CARRIER_CONFIG_SERVICE);
956 PersistableBundle b = null;
957 if (configManager != null) {
958 b = configManager.getConfig();
961 return b.getBoolean(key);
963 // Return static default defined in CarrierConfigManager.
964 return CarrierConfigManager.getDefaultConfig().getBoolean(key);
969 * Gets the call ID from the specified incoming call broadcast intent.
971 * @param incomingCallIntent the incoming call broadcast intent
972 * @return the call ID or null if the intent does not contain it
974 private static String getCallId(Intent incomingCallIntent) {
975 if (incomingCallIntent == null) {
979 return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
983 * Gets the service type from the specified incoming call broadcast intent.
985 * @param incomingCallIntent the incoming call broadcast intent
986 * @return the service identifier or -1 if the intent does not contain it
988 private static int getServiceId(Intent incomingCallIntent) {
989 if (incomingCallIntent == null) {
993 return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
997 * Binds the IMS service only if the service is not created.
999 private void checkAndThrowExceptionIfServiceUnavailable()
1000 throws ImsException {
1001 if (mImsService == null) {
1002 createImsService(true);
1004 if (mImsService == null) {
1005 throw new ImsException("Service is unavailable",
1006 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1011 private static String getImsServiceName(int phoneId) {
1012 // TODO: MSIM implementation needs to decide on service name as a function of phoneId
1017 * Binds the IMS service to make/receive the call.
1019 private void createImsService(boolean checkService) {
1021 IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
1023 if (binder == null) {
1028 IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
1032 b.linkToDeath(mDeathRecipient, 0);
1033 } catch (RemoteException e) {
1037 mImsService = IImsService.Stub.asInterface(b);
1041 * Creates a {@link ImsCallSession} with the specified call profile.
1042 * Use other methods, if applicable, instead of interacting with
1043 * {@link ImsCallSession} directly.
1045 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1046 * @param profile a call profile to make the call
1048 private ImsCallSession createCallSession(int serviceId,
1049 ImsCallProfile profile) throws ImsException {
1051 return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
1052 } catch (RemoteException e) {
1057 private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
1058 ImsConnectionStateListener listener) {
1059 ImsRegistrationListenerProxy proxy =
1060 new ImsRegistrationListenerProxy(serviceClass, listener);
1064 private static void log(String s) {
1068 private static void loge(String s) {
1072 private static void loge(String s, Throwable t) {
1077 * Used for turning on IMS.if its off already
1079 private void turnOnIms() throws ImsException {
1080 checkAndThrowExceptionIfServiceUnavailable();
1083 mImsService.turnOnIms(mPhoneId);
1084 } catch (RemoteException e) {
1085 throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1089 private boolean isImsTurnOffAllowed() {
1090 return getBooleanCarrierConfig(mContext,
1091 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)
1092 && (!isWfcEnabledByPlatform(mContext)
1093 || !isWfcEnabledByUser(mContext));
1096 private void setAdvanced4GMode(boolean turnOn) throws ImsException {
1097 checkAndThrowExceptionIfServiceUnavailable();
1100 ImsConfig config = getConfigInterface();
1101 if (config != null && (turnOn || !isImsTurnOffAllowed())) {
1102 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
1103 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
1104 if (isVtEnabledByPlatform(mContext)) {
1105 // TODO: once VT is available on platform:
1106 // - replace the '1' with the current user configuration of VT.
1107 // - separate exception checks for setFeatureValue() failures for VoLTE and VT.
1108 // I.e. if VoLTE fails still try to configure VT.
1109 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
1110 TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
1113 } catch (ImsException e) {
1114 log("setAdvanced4GMode() : " + e);
1118 } else if (isImsTurnOffAllowed()) {
1119 log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms");
1125 * Used for turning off IMS completely in order to make the device CSFB'ed.
1126 * Once turned off, all calls will be over CS.
1128 private void turnOffIms() throws ImsException {
1129 checkAndThrowExceptionIfServiceUnavailable();
1132 mImsService.turnOffIms(mPhoneId);
1133 } catch (RemoteException e) {
1134 throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1139 * Death recipient class for monitoring IMS service.
1141 private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
1143 public void binderDied() {
1149 if (mContext != null) {
1150 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
1151 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
1152 mContext.sendBroadcast(new Intent(intent));
1158 * Adapter class for {@link IImsRegistrationListener}.
1160 private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
1161 private int mServiceClass;
1162 private ImsConnectionStateListener mListener;
1164 public ImsRegistrationListenerProxy(int serviceClass,
1165 ImsConnectionStateListener listener) {
1166 mServiceClass = serviceClass;
1167 mListener = listener;
1170 public boolean isSameProxy(int serviceClass) {
1171 return (mServiceClass == serviceClass);
1175 public void registrationConnected() {
1177 log("registrationConnected ::");
1180 if (mListener != null) {
1181 mListener.onImsConnected();
1186 public void registrationProgressing() {
1188 log("registrationProgressing ::");
1191 if (mListener != null) {
1192 mListener.onImsProgressing();
1197 public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
1199 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
1202 if (mListener != null) {
1203 mListener.onImsDisconnected(imsReasonInfo);
1208 public void registrationResumed() {
1210 log("registrationResumed ::");
1213 if (mListener != null) {
1214 mListener.onImsResumed();
1219 public void registrationSuspended() {
1221 log("registrationSuspended ::");
1224 if (mListener != null) {
1225 mListener.onImsSuspended();
1230 public void registrationServiceCapabilityChanged(int serviceClass, int event) {
1231 log("registrationServiceCapabilityChanged :: serviceClass=" +
1232 serviceClass + ", event=" + event);
1234 if (mListener != null) {
1235 mListener.onImsConnected();
1240 public void registrationFeatureCapabilityChanged(int serviceClass,
1241 int[] enabledFeatures, int[] disabledFeatures) {
1242 log("registrationFeatureCapabilityChanged :: serviceClass=" +
1244 if (mListener != null) {
1245 mListener.onFeatureCapabilityChanged(serviceClass,
1246 enabledFeatures, disabledFeatures);
1251 public void voiceMessageCountUpdate(int count) {
1252 log("voiceMessageCountUpdate :: count=" + count);
1254 if (mListener != null) {
1255 mListener.onVoiceMessageCountChanged(count);
1261 * Gets the ECBM interface to request ECBM exit.
1263 * @param serviceId a service id which is obtained from {@link ImsManager#open}
1264 * @return the ECBM interface instance
1265 * @throws ImsException if getting the ECBM interface results in an error
1267 public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
1268 if (mEcbm == null) {
1269 checkAndThrowExceptionIfServiceUnavailable();
1272 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
1274 if (iEcbm == null) {
1275 throw new ImsException("getEcbmInterface()",
1276 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
1278 mEcbm = new ImsEcbm(iEcbm);
1279 } catch (RemoteException e) {
1280 throw new ImsException("getEcbmInterface()", e,
1281 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1288 * Resets ImsManager settings back to factory defaults.
1292 public static void factoryReset(Context context) {
1293 // Set VoLTE to default
1294 android.provider.Settings.Global.putInt(context.getContentResolver(),
1295 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
1296 ImsConfig.FeatureValueConstants.ON);
1298 // Set VoWiFi to default
1299 android.provider.Settings.Global.putInt(context.getContentResolver(),
1300 android.provider.Settings.Global.WFC_IMS_ENABLED,
1301 ImsConfig.FeatureValueConstants.OFF);
1303 // Set VoWiFi mode to default
1304 android.provider.Settings.Global.putInt(context.getContentResolver(),
1305 android.provider.Settings.Global.WFC_IMS_MODE,
1306 ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED);
1308 // Set VoWiFi roaming to default
1309 android.provider.Settings.Global.putInt(context.getContentResolver(),
1310 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1311 ImsConfig.FeatureValueConstants.OFF);
1313 // Set VT to default
1314 SharedPreferences sharedPrefs =
1315 PreferenceManager.getDefaultSharedPreferences(context);
1316 SharedPreferences.Editor editor = sharedPrefs.edit();
1317 editor.putBoolean(PREF_ENABLE_VIDEO_CALLING_KEY, true);
1320 // Push settings to ImsConfig
1321 ImsManager.updateImsServiceConfig(context,
1322 SubscriptionManager.getDefaultVoicePhoneId(), true);