diff --git a/prebuilt/common/app/Substratum/Substratum.apk b/prebuilt/common/app/Substratum/Substratum.apk index 5e2434c..f1d98e0 100644 Binary files a/prebuilt/common/app/Substratum/Substratum.apk and b/prebuilt/common/app/Substratum/Substratum.apk differ diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java deleted file mode 100644 index f154f1f..0000000 --- a/src/java/com/android/ims/ImsManager.java +++ /dev/null @@ -1,1364 +0,0 @@ -/* - * Copyright (c) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ims; - -import android.app.PendingIntent; -import android.app.QueuedWork; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.IBinder; -import android.os.Message; -import android.os.PersistableBundle; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemProperties; -import android.preference.PreferenceManager; -import android.provider.Settings; -import android.telecom.TelecomManager; -import android.telephony.CarrierConfigManager; -import android.telephony.Rlog; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; - -import com.android.ims.internal.IImsCallSession; -import com.android.ims.internal.IImsEcbm; -import com.android.ims.internal.IImsEcbmListener; -import com.android.ims.internal.IImsRegistrationListener; -import com.android.ims.internal.IImsService; -import com.android.ims.internal.IImsUt; -import com.android.ims.internal.ImsCallSession; -import com.android.ims.internal.IImsConfig; - -import com.android.internal.telephony.TelephonyProperties; - -import java.util.HashMap; - -/** - * Provides APIs for IMS services, such as initiating IMS calls, and provides access to - * the operator's IMS network. This class is the starting point for any IMS actions. - * You can acquire an instance of it with {@link #getInstance getInstance()}.

- *

The APIs in this class allows you to:

- * - * @hide - */ -public class ImsManager { - - /* - * Debug flag to override configuration flag - */ - public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr"; - public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0; - public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr"; - public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0; - public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr"; - public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0; - - /** - * For accessing the IMS related service. - * Internal use only. - * @hide - */ - private static final String IMS_SERVICE = "ims"; - - /** - * The result code to be sent back with the incoming call {@link PendingIntent}. - * @see #open(PendingIntent, ImsConnectionStateListener) - */ - public static final int INCOMING_CALL_RESULT_CODE = 101; - - /** - * Key to retrieve the call ID from an incoming call intent. - * @see #open(PendingIntent, ImsConnectionStateListener) - */ - public static final String EXTRA_CALL_ID = "android:imsCallID"; - - /** - * Action to broadcast when ImsService is up. - * Internal use only. - * @hide - */ - public static final String ACTION_IMS_SERVICE_UP = - "com.android.ims.IMS_SERVICE_UP"; - - /** - * Action to broadcast when ImsService is down. - * Internal use only. - * @hide - */ - public static final String ACTION_IMS_SERVICE_DOWN = - "com.android.ims.IMS_SERVICE_DOWN"; - - /** - * Action to broadcast when ImsService registration fails. - * Internal use only. - * @hide - */ - public static final String ACTION_IMS_REGISTRATION_ERROR = - "com.android.ims.REGISTRATION_ERROR"; - - /** - * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents. - * A long value; the phone ID corresponding to the IMS service coming up or down. - * Internal use only. - * @hide - */ - public static final String EXTRA_PHONE_ID = "android:phone_id"; - - /** - * Action for the incoming call intent for the Phone app. - * Internal use only. - * @hide - */ - public static final String ACTION_IMS_INCOMING_CALL = - "com.android.ims.IMS_INCOMING_CALL"; - - /** - * Part of the ACTION_IMS_INCOMING_CALL intents. - * An integer value; service identifier obtained from {@link ImsManager#open}. - * Internal use only. - * @hide - */ - public static final String EXTRA_SERVICE_ID = "android:imsServiceId"; - - /** - * Part of the ACTION_IMS_INCOMING_CALL intents. - * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD. - * The value "true" indicates that the incoming call is for USSD. - * Internal use only. - * @hide - */ - public static final String EXTRA_USSD = "android:ussd"; - - /** - * Part of the ACTION_IMS_INCOMING_CALL intents. - * A boolean value; Flag to indicate whether the call is an unknown - * dialing call. Such calls are originated by sending commands (like - * AT commands) directly to modem without Android involvement. - * Even though they are not incoming calls, they are propagated - * to Phone app using same ACTION_IMS_INCOMING_CALL intent. - * Internal use only. - * @hide - */ - public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown"; - - private static final String TAG = "ImsManager"; - private static final boolean DBG = true; - - private static HashMap sImsManagerInstances = - new HashMap(); - - private Context mContext; - private int mPhoneId; - private IImsService mImsService = null; - private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient(); - // Ut interface for the supplementary service configuration - private ImsUt mUt = null; - // Interface to get/set ims config items - private ImsConfig mConfig = null; - private boolean mConfigUpdated = false; - private static final String PREF_ENABLE_VIDEO_CALLING_KEY = "enable_video_calling"; - - // ECBM interface - private ImsEcbm mEcbm = null; - - /** - * Gets a manager instance. - * - * @param context application context for creating the manager object - * @param phoneId the phone ID for the IMS Service - * @return the manager instance corresponding to the phoneId - */ - public static ImsManager getInstance(Context context, int phoneId) { - synchronized (sImsManagerInstances) { - if (sImsManagerInstances.containsKey(phoneId)) - return sImsManagerInstances.get(phoneId); - - ImsManager mgr = new ImsManager(context, phoneId); - sImsManagerInstances.put(phoneId, mgr); - - return mgr; - } - } - - /** - * Returns the user configuration of Enhanced 4G LTE Mode setting - */ - public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) { - int enabled = android.provider.Settings.Global.getInt( - context.getContentResolver(), - android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON); - return (enabled == 1) ? true : false; - } - - /** - * Change persistent Enhanced 4G LTE Mode setting - */ - public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) { - int value = enabled ? 1 : 0; - android.provider.Settings.Global.putInt( - context.getContentResolver(), - android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value); - - if (isNonTtyOrTtyOnVolteEnabled(context)) { - ImsManager imsManager = ImsManager.getInstance(context, - SubscriptionManager.getDefaultVoicePhoneId()); - if (imsManager != null) { - try { - imsManager.setAdvanced4GMode(enabled); - } catch (ImsException ie) { - // do nothing - } - } - } - } - - /** - * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is - * supported. - */ - public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) { - if (getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) { - return true; - } - - return Settings.Secure.getInt(context.getContentResolver(), - Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF) - == TelecomManager.TTY_MODE_OFF; - } - - /** - * Returns a platform configuration for VoLTE which may override the user setting. - */ - public static boolean isVolteEnabledByPlatform(Context context) { - if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE, - PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) { - return true; - } - - return context.getResources().getBoolean( - com.android.internal.R.bool.config_device_volte_available) - && getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL) - && isGbaValid(context); - } - - /* - * Indicates whether VoLTE is provisioned on device - */ - public static boolean isVolteProvisionedOnDevice(Context context) { - return isVolteProvisionedOnDevice(context, SubscriptionManager.getDefaultVoicePhoneId()); - } - - /** - * @hide - */ - public static boolean isVolteProvisionedOnDevice(Context context, int phoneId) { - boolean isProvisioned = true; - if (getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) { - isProvisioned = false; // disable on any error - ImsManager mgr = ImsManager.getInstance(context, phoneId); - if (mgr != null) { - try { - ImsConfig config = mgr.getConfigInterface(); - if (config != null) { - isProvisioned = config.getVolteProvisioned(); - } - } catch (ImsException ie) { - // do nothing - } - } - } - - return isProvisioned; - } - - /** - * Returns a platform configuration for VT which may override the user setting. - * - * Note: VT presumes that VoLTE is enabled (these are configuration settings - * which must be done correctly). - */ - public static boolean isVtEnabledByPlatform(Context context) { - if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE, - PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) { - return true; - } - - return - context.getResources().getBoolean( - com.android.internal.R.bool.config_device_vt_available) && - getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) && - isGbaValid(context); - } - - /** - * Returns the user configuration of VT setting - */ - public static boolean isVtEnabledByUser(Context context) { - int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(), - android.provider.Settings.Global.VT_IMS_ENABLED, - ImsConfig.FeatureValueConstants.ON); - return (enabled == 1) ? true : false; - } - - /** - * Change persistent VT enabled setting - */ - public static void setVtSetting(Context context, boolean enabled) { - int value = enabled ? 1 : 0; - android.provider.Settings.Global.putInt(context.getContentResolver(), - android.provider.Settings.Global.VT_IMS_ENABLED, value); - - ImsManager imsManager = ImsManager.getInstance(context, - SubscriptionManager.getDefaultVoicePhoneId()); - if (imsManager != null) { - try { - ImsConfig config = imsManager.getConfigInterface(); - config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE, - TelephonyManager.NETWORK_TYPE_LTE, - enabled ? ImsConfig.FeatureValueConstants.ON - : ImsConfig.FeatureValueConstants.OFF, null); - - if (enabled) { - imsManager.turnOnIms(); - } else if (context.getResources().getBoolean( - com.android.internal.R.bool.imsServiceAllowTurnOff) && ( - !isVolteEnabledByPlatform(context) - || !isEnhanced4gLteModeSettingEnabledByUser(context))) { - log("setVtSetting() : imsServiceAllowTurnOff -> turnOffIms"); - imsManager.turnOffIms(); - } - } catch (ImsException e) { - loge("setVtSetting(): " + e); - } - } - } - - /** - * Returns the user configuration of WFC setting - */ - public static boolean isWfcEnabledByUser(Context context) { - int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(), - android.provider.Settings.Global.WFC_IMS_ENABLED, - ImsConfig.FeatureValueConstants.OFF); - return (enabled == 1) ? true : false; - } - - /** - * Change persistent WFC enabled setting - */ - public static void setWfcSetting(Context context, boolean enabled) { - int value = enabled ? 1 : 0; - android.provider.Settings.Global.putInt(context.getContentResolver(), - android.provider.Settings.Global.WFC_IMS_ENABLED, value); - - ImsManager imsManager = ImsManager.getInstance(context, - SubscriptionManager.getDefaultVoicePhoneId()); - if (imsManager != null) { - try { - ImsConfig config = imsManager.getConfigInterface(); - config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI, - TelephonyManager.NETWORK_TYPE_IWLAN, - enabled ? ImsConfig.FeatureValueConstants.ON - : ImsConfig.FeatureValueConstants.OFF, null); - - if (enabled) { - imsManager.turnOnIms(); - } else if (getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL) - && (!isVolteEnabledByPlatform(context) - || !isEnhanced4gLteModeSettingEnabledByUser(context))) { - log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms"); - imsManager.turnOffIms(); - } - - // Force IMS to register over LTE when turning off WFC - setWfcModeInternal(context, enabled - ? getWfcMode(context) - : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED); - } catch (ImsException e) { - loge("setWfcSetting(): " + e); - } - } - } - - /** - * Returns the user configuration of WFC modem setting - */ - public static int getWfcMode(Context context) { - int setting = android.provider.Settings.Global.getInt(context.getContentResolver(), - android.provider.Settings.Global.WFC_IMS_MODE, - ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED); - if (DBG) log("getWfcMode - setting=" + setting); - return setting; - } - - /** - * Returns the user configuration of WFC modem setting - */ - public static void setWfcMode(Context context, int wfcMode) { - if (DBG) log("setWfcMode - setting=" + wfcMode); - android.provider.Settings.Global.putInt(context.getContentResolver(), - android.provider.Settings.Global.WFC_IMS_MODE, wfcMode); - - setWfcModeInternal(context, wfcMode); - } - - private static void setWfcModeInternal(Context context, int wfcMode) { - final ImsManager imsManager = ImsManager.getInstance(context, - SubscriptionManager.getDefaultVoicePhoneId()); - if (imsManager != null) { - final int value = wfcMode; - QueuedWork.singleThreadExecutor().submit(new Runnable() { - public void run() { - try { - imsManager.getConfigInterface().setProvisionedValue( - ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE, - value); - } catch (ImsException e) { - // do nothing - } - } - }); - } - } - - /** - * Returns the user configuration of WFC roaming setting - */ - public static boolean isWfcRoamingEnabledByUser(Context context) { - int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(), - android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED, - ImsConfig.FeatureValueConstants.OFF); - return (enabled == 1) ? true : false; - } - - /** - * Change persistent WFC roaming enabled setting - */ - public static void setWfcRoamingSetting(Context context, boolean enabled) { - android.provider.Settings.Global.putInt(context.getContentResolver(), - android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED, - enabled - ? ImsConfig.FeatureValueConstants.ON - : ImsConfig.FeatureValueConstants.OFF); - - setWfcRoamingSettingInternal(context, enabled); - } - - private static void setWfcRoamingSettingInternal(Context context, boolean enabled) { - final ImsManager imsManager = ImsManager.getInstance(context, - SubscriptionManager.getDefaultVoicePhoneId()); - if (imsManager != null) { - final int value = enabled - ? ImsConfig.FeatureValueConstants.ON - : ImsConfig.FeatureValueConstants.OFF; - QueuedWork.singleThreadExecutor().submit(new Runnable() { - public void run() { - try { - imsManager.getConfigInterface().setProvisionedValue( - ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING, - value); - } catch (ImsException e) { - // do nothing - } - } - }); - } - } - - /** - * Returns a platform configuration for WFC which may override the user - * setting. Note: WFC presumes that VoLTE is enabled (these are - * configuration settings which must be done correctly). - */ - public static boolean isWfcEnabledByPlatform(Context context) { - if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE, - PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) { - return true; - } - - return - context.getResources().getBoolean( - com.android.internal.R.bool.config_device_wfc_ims_available) && - getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) && - isGbaValid(context); - } - - /** - * If carrier requires that IMS is only available if GBA capable SIM is used, - * then this function checks GBA bit in EF IST. - * - * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7). - */ - private static boolean isGbaValid(Context context) { - if (getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) { - final TelephonyManager telephonyManager = TelephonyManager.getDefault(); - String efIst = telephonyManager.getIsimIst(); - if (efIst == null) { - loge("ISF is NULL"); - return true; - } - boolean result = efIst != null && efIst.length() > 1 && - (0x02 & (byte)efIst.charAt(1)) != 0; - if (DBG) log("GBA capable=" + result + ", ISF=" + efIst); - return result; - } - return true; - } - - /** - * Sync carrier config and user settings with ImsConfig. - * - * @param context for the manager object - * @param phoneId phone id - * @param force update - */ - public static void updateImsServiceConfig(Context context, int phoneId, boolean force) { - final ImsManager imsManager = ImsManager.getInstance(context, phoneId); - if (imsManager != null && (!imsManager.mConfigUpdated || force)) { - try { - boolean turnOn = imsManager.updateVolteFeatureValue(); - turnOn |= imsManager.updateVideoCallFeatureValue(); - turnOn |= imsManager.updateWfcFeatureAndProvisionedValues(); - - if (turnOn) { - imsManager.turnOnIms(); - } else if (getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)) { - imsManager.turnOffIms(); - } - - imsManager.mConfigUpdated = true; - } catch (ImsException e) { - loge("updateImsServiceConfig: " + e); - imsManager.mConfigUpdated = false; - } - } - } - - /** - * Update VoLTE config - * @return whether feature is On - * @throws ImsException - */ - private boolean updateVolteFeatureValue() throws ImsException { - boolean available = isVolteEnabledByPlatform(mContext); - boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext); - boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext); - boolean turnOn = available && enabled && isNonTty; - - log("updateVolteFeatureValue: available = " + available - + ", enabled = " + enabled - + ", nonTTY = " + isNonTty); - - getConfigInterface().setFeatureValue( - ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, - TelephonyManager.NETWORK_TYPE_LTE, - turnOn ? - ImsConfig.FeatureValueConstants.ON : - ImsConfig.FeatureValueConstants.OFF, - null); - - return turnOn; - } - - /** - * Update VC config - * @return whether feature is On - * @throws ImsException - */ - private boolean updateVideoCallFeatureValue() throws ImsException { - boolean available = isVtEnabledByPlatform(mContext); - SharedPreferences sharedPrefs = - PreferenceManager.getDefaultSharedPreferences(mContext); - boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext) && - sharedPrefs.getBoolean(PREF_ENABLE_VIDEO_CALLING_KEY, true); - boolean isNonTty = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF) - == TelecomManager.TTY_MODE_OFF; - boolean turnOn = available && enabled && isNonTty; - - log("updateVideoCallFeatureValue: available = " + available - + ", enabled = " + enabled - + ", nonTTY = " + isNonTty); - - getConfigInterface().setFeatureValue( - ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE, - TelephonyManager.NETWORK_TYPE_LTE, - turnOn ? - ImsConfig.FeatureValueConstants.ON : - ImsConfig.FeatureValueConstants.OFF, - null); - - return turnOn; - } - - /** - * Update WFC config - * @return whether feature is On - * @throws ImsException - */ - private boolean updateWfcFeatureAndProvisionedValues() throws ImsException { - boolean available = isWfcEnabledByPlatform(mContext); - boolean enabled = isWfcEnabledByUser(mContext); - int mode = getWfcMode(mContext); - boolean roaming = isWfcRoamingEnabledByUser(mContext); - boolean turnOn = available && enabled; - - log("updateWfcFeatureAndProvisionedValues: available = " + available - + ", enabled = " + enabled - + ", mode = " + mode - + ", roaming = " + roaming); - - getConfigInterface().setFeatureValue( - ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI, - TelephonyManager.NETWORK_TYPE_IWLAN, - turnOn ? - ImsConfig.FeatureValueConstants.ON : - ImsConfig.FeatureValueConstants.OFF, - null); - - if (!turnOn) { - mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED; - roaming = false; - } - setWfcModeInternal(mContext, mode); - setWfcRoamingSettingInternal(mContext, roaming); - - return turnOn; - } - - private ImsManager(Context context, int phoneId) { - mContext = context; - mPhoneId = phoneId; - createImsService(true); - } - - /* - * Returns a flag indicating whether the IMS service is available. - */ - public boolean isServiceAvailable() { - if (mImsService != null) { - return true; - } - - IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); - if (binder != null) { - return true; - } - - return false; - } - - /** - * Opens the IMS service for making calls and/or receiving generic IMS calls. - * The caller may make subsquent calls through {@link #makeCall}. - * The IMS service will register the device to the operator's network with the credentials - * (from ISIM) periodically in order to receive calls from the operator's network. - * When the IMS service receives a new call, it will send out an intent with - * the provided action string. - * The intent contains a call ID extra {@link getCallId} and it can be used to take a call. - * - * @param serviceClass a service class specified in {@link ImsServiceClass} - * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}. - * @param incomingCallPendingIntent When an incoming call is received, - * the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to - * send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} - * as the result code and the intent to fill in the call ID; It cannot be null - * @param listener To listen to IMS registration events; It cannot be null - * @return identifier (greater than 0) for the specified service - * @throws NullPointerException if {@code incomingCallPendingIntent} - * or {@code listener} is null - * @throws ImsException if calling the IMS service results in an error - * @see #getCallId - * @see #getServiceId - */ - public int open(int serviceClass, PendingIntent incomingCallPendingIntent, - ImsConnectionStateListener listener) throws ImsException { - checkAndThrowExceptionIfServiceUnavailable(); - - if (incomingCallPendingIntent == null) { - throw new NullPointerException("incomingCallPendingIntent can't be null"); - } - - if (listener == null) { - throw new NullPointerException("listener can't be null"); - } - - int result = 0; - - try { - result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent, - createRegistrationListenerProxy(serviceClass, listener)); - } catch (RemoteException e) { - throw new ImsException("open()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - - if (result <= 0) { - // If the return value is a minus value, - // it means that an error occurred in the service. - // So, it needs to convert to the reason code specified in ImsReasonInfo. - throw new ImsException("open()", (result * (-1))); - } - - return result; - } - - /** - * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls. - * All the resources that were allocated to the service are also released. - * - * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open} - * @throws ImsException if calling the IMS service results in an error - */ - public void close(int serviceId) throws ImsException { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - mImsService.close(serviceId); - } catch (RemoteException e) { - throw new ImsException("close()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } finally { - mUt = null; - mConfig = null; - mEcbm = null; - } - } - - /** - * Gets the configuration interface to provision / withdraw the supplementary service settings. - * - * @param serviceId a service id which is obtained from {@link ImsManager#open} - * @return the Ut interface instance - * @throws ImsException if getting the Ut interface results in an error - */ - public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId) - throws ImsException { - // FIXME: manage the multiple Ut interfaces based on the service id - if (mUt == null) { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - IImsUt iUt = mImsService.getUtInterface(serviceId); - - if (iUt == null) { - throw new ImsException("getSupplementaryServiceConfiguration()", - ImsReasonInfo.CODE_UT_NOT_SUPPORTED); - } - - mUt = new ImsUt(iUt); - } catch (RemoteException e) { - throw new ImsException("getSupplementaryServiceConfiguration()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - - return mUt; - } - - /** - * Checks if the IMS service has successfully registered to the IMS network - * with the specified service & call type. - * - * @param serviceId a service id which is obtained from {@link ImsManager#open} - * @param serviceType a service type that is specified in {@link ImsCallProfile} - * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} - * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} - * @param callType a call type that is specified in {@link ImsCallProfile} - * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO} - * {@link ImsCallProfile#CALL_TYPE_VOICE} - * {@link ImsCallProfile#CALL_TYPE_VT} - * {@link ImsCallProfile#CALL_TYPE_VS} - * @return true if the specified service id is connected to the IMS network; - * false otherwise - * @throws ImsException if calling the IMS service results in an error - */ - public boolean isConnected(int serviceId, int serviceType, int callType) - throws ImsException { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - return mImsService.isConnected(serviceId, serviceType, callType); - } catch (RemoteException e) { - throw new ImsException("isServiceConnected()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - - /** - * Checks if the specified IMS service is opend. - * - * @param serviceId a service id which is obtained from {@link ImsManager#open} - * @return true if the specified service id is opened; false otherwise - * @throws ImsException if calling the IMS service results in an error - */ - public boolean isOpened(int serviceId) throws ImsException { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - return mImsService.isOpened(serviceId); - } catch (RemoteException e) { - throw new ImsException("isOpened()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - - /** - * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. - * - * @param serviceId a service id which is obtained from {@link ImsManager#open} - * @param serviceType a service type that is specified in {@link ImsCallProfile} - * {@link ImsCallProfile#SERVICE_TYPE_NONE} - * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} - * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} - * @param callType a call type that is specified in {@link ImsCallProfile} - * {@link ImsCallProfile#CALL_TYPE_VOICE} - * {@link ImsCallProfile#CALL_TYPE_VT} - * {@link ImsCallProfile#CALL_TYPE_VT_TX} - * {@link ImsCallProfile#CALL_TYPE_VT_RX} - * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} - * {@link ImsCallProfile#CALL_TYPE_VS} - * {@link ImsCallProfile#CALL_TYPE_VS_TX} - * {@link ImsCallProfile#CALL_TYPE_VS_RX} - * @return a {@link ImsCallProfile} object - * @throws ImsException if calling the IMS service results in an error - */ - public ImsCallProfile createCallProfile(int serviceId, - int serviceType, int callType) throws ImsException { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - return mImsService.createCallProfile(serviceId, serviceType, callType); - } catch (RemoteException e) { - throw new ImsException("createCallProfile()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - - /** - * Creates a {@link ImsCall} to make a call. - * - * @param serviceId a service id which is obtained from {@link ImsManager#open} - * @param profile a call profile to make the call - * (it contains service type, call type, media information, etc.) - * @param participants participants to invite the conference call - * @param listener listen to the call events from {@link ImsCall} - * @return a {@link ImsCall} object - * @throws ImsException if calling the IMS service results in an error - */ - public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees, - ImsCall.Listener listener) throws ImsException { - if (DBG) { - log("makeCall :: serviceId=" + serviceId - + ", profile=" + profile + ", callees=" + callees); - } - - checkAndThrowExceptionIfServiceUnavailable(); - - ImsCall call = new ImsCall(mContext, profile); - - call.setListener(listener); - ImsCallSession session = createCallSession(serviceId, profile); - boolean isConferenceUri = profile.getCallExtraBoolean( - TelephonyProperties.EXTRAS_IS_CONFERENCE_URI, false); - if (!isConferenceUri && (callees != null) && (callees.length == 1)) { - call.start(session, callees[0]); - } else { - call.start(session, callees); - } - - return call; - } - - /** - * Creates a {@link ImsCall} to take an incoming call. - * - * @param serviceId a service id which is obtained from {@link ImsManager#open} - * @param incomingCallIntent the incoming call broadcast intent - * @param listener to listen to the call events from {@link ImsCall} - * @return a {@link ImsCall} object - * @throws ImsException if calling the IMS service results in an error - */ - public ImsCall takeCall(int serviceId, Intent incomingCallIntent, - ImsCall.Listener listener) throws ImsException { - if (DBG) { - log("takeCall :: serviceId=" + serviceId - + ", incomingCall=" + incomingCallIntent); - } - - checkAndThrowExceptionIfServiceUnavailable(); - - if (incomingCallIntent == null) { - throw new ImsException("Can't retrieve session with null intent", - ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); - } - - int incomingServiceId = getServiceId(incomingCallIntent); - - if (serviceId != incomingServiceId) { - throw new ImsException("Service id is mismatched in the incoming call intent", - ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); - } - - String callId = getCallId(incomingCallIntent); - - if (callId == null) { - throw new ImsException("Call ID missing in the incoming call intent", - ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT); - } - - try { - IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId); - - if (session == null) { - throw new ImsException("No pending session for the call", - ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL); - } - - ImsCall call = new ImsCall(mContext, session.getCallProfile()); - - call.attachSession(new ImsCallSession(session)); - call.setListener(listener); - - return call; - } catch (Throwable t) { - throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED); - } - } - - /** - * Gets the config interface to get/set service/capability parameters. - * - * @return the ImsConfig instance. - * @throws ImsException if getting the setting interface results in an error. - */ - public ImsConfig getConfigInterface() throws ImsException { - - if (mConfig == null) { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - IImsConfig config = mImsService.getConfigInterface(mPhoneId); - if (config == null) { - throw new ImsException("getConfigInterface()", - ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); - } - mConfig = new ImsConfig(config, mContext); - } catch (RemoteException e) { - throw new ImsException("getConfigInterface()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - if (DBG) log("getConfigInterface(), mConfig= " + mConfig); - return mConfig; - } - - public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete) - throws ImsException { - - checkAndThrowExceptionIfServiceUnavailable(); - - try { - mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete); - } catch (RemoteException e) { - throw new ImsException("setTTYMode()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - - if (!getBooleanCarrierConfig(context, - CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) { - setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) && - isEnhanced4gLteModeSettingEnabledByUser(context)); - } - } - - /** - * Get the boolean config from carrier config manager. - * - * @param context the context to get carrier service - * @param key config key defined in CarrierConfigManager - * @return boolean value of corresponding key. - */ - private static boolean getBooleanCarrierConfig(Context context, String key) { - CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService( - Context.CARRIER_CONFIG_SERVICE); - PersistableBundle b = null; - if (configManager != null) { - b = configManager.getConfig(); - } - if (b != null) { - return b.getBoolean(key); - } else { - // Return static default defined in CarrierConfigManager. - return CarrierConfigManager.getDefaultConfig().getBoolean(key); - } - } - - /** - * Gets the call ID from the specified incoming call broadcast intent. - * - * @param incomingCallIntent the incoming call broadcast intent - * @return the call ID or null if the intent does not contain it - */ - private static String getCallId(Intent incomingCallIntent) { - if (incomingCallIntent == null) { - return null; - } - - return incomingCallIntent.getStringExtra(EXTRA_CALL_ID); - } - - /** - * Gets the service type from the specified incoming call broadcast intent. - * - * @param incomingCallIntent the incoming call broadcast intent - * @return the service identifier or -1 if the intent does not contain it - */ - private static int getServiceId(Intent incomingCallIntent) { - if (incomingCallIntent == null) { - return (-1); - } - - return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1); - } - - /** - * Binds the IMS service only if the service is not created. - */ - private void checkAndThrowExceptionIfServiceUnavailable() - throws ImsException { - if (mImsService == null) { - createImsService(true); - - if (mImsService == null) { - throw new ImsException("Service is unavailable", - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - } - - private static String getImsServiceName(int phoneId) { - // TODO: MSIM implementation needs to decide on service name as a function of phoneId - return IMS_SERVICE; - } - - /** - * Binds the IMS service to make/receive the call. - */ - private void createImsService(boolean checkService) { - if (checkService) { - IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); - - if (binder == null) { - return; - } - } - - IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId)); - - if (b != null) { - try { - b.linkToDeath(mDeathRecipient, 0); - } catch (RemoteException e) { - } - } - - mImsService = IImsService.Stub.asInterface(b); - } - - /** - * Creates a {@link ImsCallSession} with the specified call profile. - * Use other methods, if applicable, instead of interacting with - * {@link ImsCallSession} directly. - * - * @param serviceId a service id which is obtained from {@link ImsManager#open} - * @param profile a call profile to make the call - */ - private ImsCallSession createCallSession(int serviceId, - ImsCallProfile profile) throws ImsException { - try { - return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null)); - } catch (RemoteException e) { - return null; - } - } - - private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass, - ImsConnectionStateListener listener) { - ImsRegistrationListenerProxy proxy = - new ImsRegistrationListenerProxy(serviceClass, listener); - return proxy; - } - - private static void log(String s) { - Rlog.d(TAG, s); - } - - private static void loge(String s) { - Rlog.e(TAG, s); - } - - private static void loge(String s, Throwable t) { - Rlog.e(TAG, s, t); - } - - /** - * Used for turning on IMS.if its off already - */ - private void turnOnIms() throws ImsException { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - mImsService.turnOnIms(mPhoneId); - } catch (RemoteException e) { - throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - - private boolean isImsTurnOffAllowed() { - return getBooleanCarrierConfig(mContext, - CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL) - && (!isWfcEnabledByPlatform(mContext) - || !isWfcEnabledByUser(mContext)); - } - - private void setAdvanced4GMode(boolean turnOn) throws ImsException { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - ImsConfig config = getConfigInterface(); - if (config != null && (turnOn || !isImsTurnOffAllowed())) { - config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, - TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null); - if (isVtEnabledByPlatform(mContext)) { - // TODO: once VT is available on platform: - // - replace the '1' with the current user configuration of VT. - // - separate exception checks for setFeatureValue() failures for VoLTE and VT. - // I.e. if VoLTE fails still try to configure VT. - config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE, - TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null); - } - } - } catch (ImsException e) { - log("setAdvanced4GMode() : " + e); - } - if (turnOn) { - turnOnIms(); - } else if (isImsTurnOffAllowed()) { - log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms"); - turnOffIms(); - } - } - - /** - * Used for turning off IMS completely in order to make the device CSFB'ed. - * Once turned off, all calls will be over CS. - */ - private void turnOffIms() throws ImsException { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - mImsService.turnOffIms(mPhoneId); - } catch (RemoteException e) { - throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - - /** - * Death recipient class for monitoring IMS service. - */ - private class ImsServiceDeathRecipient implements IBinder.DeathRecipient { - @Override - public void binderDied() { - mImsService = null; - mUt = null; - mConfig = null; - mEcbm = null; - - if (mContext != null) { - Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN); - intent.putExtra(EXTRA_PHONE_ID, mPhoneId); - mContext.sendBroadcast(new Intent(intent)); - } - } - } - - /** - * Adapter class for {@link IImsRegistrationListener}. - */ - private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub { - private int mServiceClass; - private ImsConnectionStateListener mListener; - - public ImsRegistrationListenerProxy(int serviceClass, - ImsConnectionStateListener listener) { - mServiceClass = serviceClass; - mListener = listener; - } - - public boolean isSameProxy(int serviceClass) { - return (mServiceClass == serviceClass); - } - - @Override - public void registrationConnected(int imsRadioTech) { - // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY - // values in ServiceState.java. - if (DBG) { - log("registrationConnected :: imsRadioTech=" + imsRadioTech); - } - - if (mListener != null) { - mListener.onImsConnected(); - } - } - - @Override - public void registrationConnected() { - if (DBG) { - log("registrationConnected ::"); - } - - if (mListener != null) { - mListener.onImsConnected(); - } - } - - @Override - public void registrationProgressing(int imsRadioTech) { - // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY - // values in ServiceState.java. - if (DBG) { - log("registrationProgressing :: imsRadioTech=" + imsRadioTech); - } - - if (mListener != null) { - mListener.onImsProgressing(); - } - } - - @Override - public void registrationProgressing() { - if (DBG) { - log("registrationProgressing ::"); - } - - if (mListener != null) { - mListener.onImsProgressing(); - } - } - - @Override - public void registrationDisconnected(ImsReasonInfo imsReasonInfo) { - if (DBG) { - log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo); - } - - if (mListener != null) { - mListener.onImsDisconnected(imsReasonInfo); - } - } - - @Override - public void registrationResumed() { - if (DBG) { - log("registrationResumed ::"); - } - - if (mListener != null) { - mListener.onImsResumed(); - } - } - - @Override - public void registrationSuspended() { - if (DBG) { - log("registrationSuspended ::"); - } - - if (mListener != null) { - mListener.onImsSuspended(); - } - } - - @Override - public void registrationServiceCapabilityChanged(int serviceClass, int event) { - log("registrationServiceCapabilityChanged :: serviceClass=" + - serviceClass + ", event=" + event); - - if (mListener != null) { - mListener.onImsConnected(); - } - } - - @Override - public void registrationFeatureCapabilityChanged(int serviceClass, - int[] enabledFeatures, int[] disabledFeatures) { - log("registrationFeatureCapabilityChanged :: serviceClass=" + - serviceClass); - if (mListener != null) { - mListener.onFeatureCapabilityChanged(serviceClass, - enabledFeatures, disabledFeatures); - } - } - - @Override - public void voiceMessageCountUpdate(int count) { - log("voiceMessageCountUpdate :: count=" + count); - - if (mListener != null) { - mListener.onVoiceMessageCountChanged(count); - } - } - - } - /** - * Gets the ECBM interface to request ECBM exit. - * - * @param serviceId a service id which is obtained from {@link ImsManager#open} - * @return the ECBM interface instance - * @throws ImsException if getting the ECBM interface results in an error - */ - public ImsEcbm getEcbmInterface(int serviceId) throws ImsException { - if (mEcbm == null) { - checkAndThrowExceptionIfServiceUnavailable(); - - try { - IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId); - - if (iEcbm == null) { - throw new ImsException("getEcbmInterface()", - ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED); - } - mEcbm = new ImsEcbm(iEcbm); - } catch (RemoteException e) { - throw new ImsException("getEcbmInterface()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } - } - return mEcbm; - } -}