前言

最近有需求需要实现插卡默认打开Volte功能,顺带研究了下Volte的流程,在此做个记录

开始

从Settings设置界面入手,网络和互联网-->移动网络-->VoLTE高清通话(电信卡)/增强型4G LTE模式(移动卡)

找到网络和互联网加载对应的Fragment为NetworkDashboardFragment,

源码位置 vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\network\NetworkDashboardFragment.java

NetworkDashboardFragment加载的布局xml为 network_and_internet.xml

源码位置 vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\network_and_internet.xml

<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:title="@string/network_dashboard_title"> ... <com.android.settingslib.RestrictedPreference
android:key="mobile_network_settings"
android:title="@string/network_settings_title"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_network_cell"
android:dependency="toggle_airplane"
android:order="-15"
settings:keywords="@string/keywords_more_mobile_networks"
settings:userRestriction="no_config_mobile_networks"
settings:useAdminDisabledSummary="true">
<intent
android:action="android.intent.action.MAIN"
android:targetPackage="com.android.phone"
android:targetClass="com.android.phone.MobileNetworkSettings"/>
</com.android.settingslib.RestrictedPreference>

从上面可以看出移动网络对应的目标启动类为MobileNetworkSettings

源码位置 vendor\mediatek\proprietary\packages\services\Telephony\src\com\android\phone\MobileNetworkSettings.java

代码不算多,2400多行,别被吓到了,只需找我们关心的即可

private void addEnhanced4GLteSwitchPreference(PreferenceScreen preferenceScreen) {
int phoneId = SubscriptionManager.getPhoneId(mPhone.getSubId());
log("[addEnhanced4GLteSwitchPreference] volteEnabled :"
+ isVolteEnabled());
if (mButton4glte != null) {
log("[addEnhanced4GLteSwitchPreference] Remove mButton4glte!");
//移除Google原生的volte开关
preferenceScreen.removePreference(mButton4glte);
}
//是否包含CT插件
boolean isCtPlugin = ExtensionManager.getMobileNetworkSettingsExt().isCtPlugin();
log("[addEnhanced4GLteSwitchPreference] ss :" + isCtPlugin);
if (isVolteEnabled() && !isCtPlugin) {
int order = mButtonEnabledNetworks.getOrder() + 1;
//实例化volte开关
mEnhancedButton4glte = new Enhanced4GLteSwitchPreference(this, mPhone.getSubId());
/// Still use Google's key, title, and summary. 将原来的key依旧设置给新的volte开关,用于处理点击事件
mEnhancedButton4glte.setKey(BUTTON_4G_LTE_KEY);
/// M: [CT VOLTE]
// show "VOLTE" for CT VOLTE SIM
if (TelephonyUtilsEx.isCtVolteEnabled()
&& TelephonyUtilsEx.isCtSim(mPhone.getSubId())) {
mEnhancedButton4glte.setTitle(R.string.hd_voice_switch_title);//VoLTE高清通话
mEnhancedButton4glte.setSummary(R.string.hd_voice_switch_summary);//启用前应先向运营商确认已开通此功能,否则可能影响正常通话
} else {
PersistableBundle carrierConfig =
PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
boolean useVariant4glteTitle = carrierConfig.getBoolean(
CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL);
int enhanced4glteModeTitleId = useVariant4glteTitle ?
R.string.enhanced_4g_lte_mode_title_variant :
R.string.enhanced_4g_lte_mode_title;//增强型 4G LTE 模式
mEnhancedButton4glte.setTitle(enhanced4glteModeTitleId);
}
/// M: [CT VOLTE]
// show "VOLTE" for CT VOLTE SIM
if (!TelephonyUtilsEx.isCtVolteEnabled()
|| !TelephonyUtilsEx.isCtSim(mPhone.getSubId())) {
/// @}
mEnhancedButton4glte.setSummary(R.string.enhanced_4g_lte_mode_summary);//使用 LTE 服务改进语音和其他通信功能(推荐)
}
mEnhancedButton4glte.setOnPreferenceChangeListener(this);
mEnhancedButton4glte.setOrder(order);
/// M: Customize the LTE switch preference. @{
ExtensionManager.getMobileNetworkSettingsExt()
.customizeEnhanced4GLteSwitchPreference(this, mEnhancedButton4glte);
/// @}
} else {
mEnhancedButton4glte = null;
}
}

开关的UI分析完了,我们接着看下volte开关的点击事件

	public boolean onPreferenceChange(Preference preference, Object objValue) {
final int phoneSubId = mPhone.getSubId();
if (onPreferenceChangeMTK(preference, objValue)) {//新实例化的volte开关处理点击事件
return true;
}
if (preference == mButtonPreferredNetworkMode) {
....
} else if (preference == mButton4glte) {//Google原生的volte点击事件
SwitchPreference enhanced4gModePref = (SwitchPreference) preference;
boolean enhanced4gMode = !enhanced4gModePref.isChecked();
enhanced4gModePref.setChecked(enhanced4gMode);
Log.e("NetworkSettings", "enhanced4gMode="+enhanced4gMode + " phoneId="+mPhone.getPhoneId());
MtkImsManager.setEnhanced4gLteModeSetting(this, enhanced4gModePref.isChecked(),
mPhone.getPhoneId());
}

新实例化的volte开关处理点击事件

private boolean onPreferenceChangeMTK(Preference preference, Object objValue) {
String volteTitle = getResources().getString(R.string.hd_voice_switch_title);
String lteTitle = getResources().getString(R.string.enhanced_4g_lte_mode_title);
log("[onPreferenceChangeMTK] Preference = " + preference.getTitle()); if ((mEnhancedButton4glte == preference) || preference.getTitle().equals(volteTitle)
|| preference.getTitle().equals(lteTitle)) {
Enhanced4GLteSwitchPreference ltePref = (Enhanced4GLteSwitchPreference) preference;
log("[onPreferenceChangeMTK] IsChecked = " + ltePref.isChecked());
/// M: [CT VOLTE] @{
if (TelephonyUtilsEx.isCtVolteEnabled() && TelephonyUtilsEx.isCtSim(
mPhone.getSubId())
&& !ltePref.isChecked()) {
int type = TelephonyManager.getDefault().getNetworkType(mPhone.getSubId());
log("network type = " + type);
if (TelephonyManager.NETWORK_TYPE_LTE != type
&& !TelephonyUtilsEx.isRoaming(mPhone)
&& (TelephonyUtilsEx.getMainPhoneId() == mPhone.getPhoneId()
|| TelephonyUtilsEx.isBothslotCt4gSim(mSubscriptionManager))) {
if (!TelephonyUtilsEx.isCtAutoVolteEnabled()) {
showVolteUnavailableDialog();
return false;
}
}
}
ltePref.setChecked(!ltePref.isChecked());
Log.e("NetworkSettings", "ltePref.isChecked()="+ltePref.isChecked() + " phoneId="+mPhone.getPhoneId());
MtkImsManager.setEnhanced4gLteModeSetting(this, ltePref.isChecked(),
mPhone.getPhoneId());
return true;
}
return false;
}

这个函数处理如果点击的是新实例化的vlote开关,或者volte整个条目,其它返回false,由原来的onPreferenceChange接着处理事件。

可以看到,最终通过MtkImsManager.setEnhanced4gLteModeSetting(this, ltePref.isChecked(), mPhone.getPhoneId()); 来控制Volte的打开和关闭。

继续跟进 源码位置 vendor\mediatek\proprietary\frameworks\opt\net\ims\src\java\com\mediatek\ims\internal\MtkImsManager.java

public static void setEnhanced4gLteModeSetting(Context context, boolean enabled, int phoneId) {
int value = enabled ? 1 : 0; if (isSupportMims() == false) {
phoneId = getMainPhoneIdForSingleIms(context);
android.provider.Settings.Global.putInt(
context.getContentResolver(),
android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value); if (isNonTtyOrTtyOnVolteEnabled(context, phoneId)) {
ImsManager imsManager = ImsManager.getInstance(context, phoneId);
if (imsManager != null) {
try {
imsManager.setAdvanced4GMode(enabled);
} catch (ImsException ie) {
// do nothing
}
}
}
} else {
sImsManagerExt = getImsManagerPluginInstance(context);
if (sImsManagerExt != null) {
phoneId = sImsManagerExt.getImsPhoneId(context, phoneId);
}
ImsManager imsManager = ImsManager.getInstance(context, phoneId);
if(imsManager != null) {
imsManager.setEnhanced4gLteModeSettingForSlot(enabled);
} else {
loge("setEnhanced4gLteModeSetting");
loge("getInstance null for phoneId=" + phoneId);
}
} }

这里需要关注下isSupportMims()的返回结果

//private static final String MULTI_IMS_SUPPORT = "persist.mtk_mims_support";
public static boolean isSupportMims() {
return (SystemProperties.getInt(MULTI_IMS_SUPPORT, 1) > 1);
}

SystemProperties读取的是build.prop中的值,经查找persist.mtk_mims_support不存在,则为默认值1, isSupportMims()结果为false,

那么回到上面的逻辑中,走if代码块,将volte的状态保存到Settings.Global.ENHANCED_4G_MODE_ENABLED中,

方便当下次进入界面时查询结果以显示开关的状态。继续看isNonTtyOrTtyOnVolteEnabled()结果

public static boolean isNonTtyOrTtyOnVolteEnabled(Context context, int phoneId) {

    if (isSupportMims() == false) {
if (ImsManager.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;
} ImsManager imsManager = ImsManager.getInstance(context, phoneId);
if(imsManager != null) {
return imsManager.isNonTtyOrTtyOnVolteEnabledForSlot();
} else {
loge("isNonTtyOrTtyOnVolteEnabled");
loge("getInstance null for phoneId=" + phoneId);
} return false;
}

从刚刚结论isSupportMims()为false,主要看ImsManager.getBooleanCarrierConfig()结果

源码位置 frameworks\opt\net\ims\src\java\com\android\ims\ImsManager.java

public 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);
}
}

从上面的代码可以看出,不论CarrierConfigManager是否为null,最终都是通过getBoolean()来读取key对应的结果,有点类似SharedPrenference

还得继续往下深入,

CarrierConfigManager 源码位置 frameworks\base\telephony\java\android\telephony\CarrierConfigManager.java

 static {
sDefaults = new PersistableBundle();
...
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true); }

从中我们找到静态代码块设置初始值 KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL,默认值为true,回到 MtkImsManager.java 中,

isNonTtyOrTtyOnVolteEnabled()结果为true,则调用imsManager.setAdvanced4GMode(enabled)来打开或关闭volte。

进入ImsManager中,源码位置 frameworks\opt\net\ims\src\java\com\android\ims\ImsManager.java

public void setAdvanced4GMode(boolean turnOn) throws ImsException {
checkAndThrowExceptionIfServiceUnavailable();//bind IMS_SERVICE,如果IMS服务不可用则抛出异常 // if turnOn: first set feature values then call turnOnIms()
// if turnOff: only set feature values if IMS turn off is not allowed. If turn off is
// allowed, first call turnOffIms() then set feature values
if (turnOn) {
setLteFeatureValues(turnOn);
log("setAdvanced4GMode: turnOnIms");
turnOnIms();//打开IMS 服务
} else {
if (isImsTurnOffAllowed()) {
log("setAdvanced4GMode: turnOffIms");
turnOffIms();//关闭IMS 服务
}
setLteFeatureValues(turnOn);
}
}

打开IMS服务

public void turnOnIms() throws ImsException {
checkAndThrowExceptionIfServiceUnavailable(); try {
mImsServiceProxy.turnOnIms();
} catch (RemoteException e) {
throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
}
}

通过mImsServiceProxy代理对象调用,代理对象的创建过程在createImsService()

/**
* Binds the IMS service to make/receive the call. Supports two methods of exposing an
* ImsService:
* 1) com.android.ims.ImsService implementation in ServiceManager (deprecated).
* 2) android.telephony.ims.ImsService implementation through ImsResolver.
*/
protected void createImsService() {
if (!mConfigDynamicBind) {
// Old method of binding
Rlog.i(TAG, "Creating ImsService using ServiceManager");
mImsServiceProxy = getServiceProxyCompat();
} else {
Rlog.i(TAG, "Creating ImsService using ImsResolver");
mImsServiceProxy = getServiceProxy();
}
// We have created a new ImsService connection, signal for re-registration
synchronized (mHasRegisteredLock) {
mHasRegisteredForProxy = false;
}
}

此处创建mImsServiceProxy代理对象有两种方式,mConfigDynamicBind的值在framework/core/res/res/values/config.xml中定义,

通过查看该值为false,则通过getServiceProxyCompat()获取mImsServiceProxy对象。

private ImsServiceProxyCompat getServiceProxyCompat() {
IBinder binder = ServiceManager.checkService(IMS_SERVICE); if (binder != null) {
try {
binder.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
}
} return new ImsServiceProxyCompat(mPhoneId, binder);
}

ImsServiceProxyCompat的turnOnIms()方法

@Override
public void turnOnIms() throws RemoteException {
checkBinderConnection();
getServiceInterface(mBinder).turnOnIms(mSlotId);
}

实际上通过mBinder获取到IImsService对象,继续跟进,实际上最终调用了IImsService.aidl的turnOnIms()

源码位置 frameworks\base\telephony\java\com\android\ims\internal\IImsService.aidl

interface IImsService {
.... /**
* Config interface to get/set IMS service/capability parameters.
*/
IImsConfig getConfigInterface(int phoneId); /**
* Used for turning on IMS when its in OFF state.
*/
void turnOnIms(int phoneId); /**
* Used for turning off IMS when its in ON state.
* When IMS is OFF, device will behave as CSFB'ed.
*/
void turnOffIms(int phoneId); ....
}

回到上面在ImsManager.java中setAdvanced4GMode()方法,不管打开或关闭都会调用setLteFeatureValues(turnOn),来看下做了什么操作

protected void setLteFeatureValues(boolean turnOn) {
log("setLteFeatureValues: " + turnOn);
try {
ImsConfig config = getConfigInterface();
if (config != null) {
config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener); if (isVolteEnabledByPlatformForSlot()) {
boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
boolean enableViLte = turnOn && isVtEnabledByUserForSlot() &&
(ignoreDataEnabledChanged || isDataEnabled());
config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
TelephonyManager.NETWORK_TYPE_LTE,
enableViLte ? 1 : 0,
mImsConfigListener);
}
}
} catch (ImsException e) {
loge("setLteFeatureValues: exception ", e);
}
}

调用ImsConfig的setFeatureValue()保存值

源码位置 frameworks\base\telephony\java\com\android\ims\ImsConfig.java

public void setFeatureValue(int feature, int network, int value,
ImsConfigListener listener) throws ImsException {
if (DBG) {
Rlog.d(TAG, "setFeatureValue: feature = " + feature + ", network =" + network +
", value =" + value + ", listener =" + listener);
}
try {
miConfig.setFeatureValue(feature, network, value, listener);
} catch (RemoteException e) {
throw new ImsException("setFeatureValue()", e,
ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
}
}

发现又调用的miConfig,继续接着找吧。调用过程:

ImsConfig.Java中setFeatureValue()--->IImsConfig.aild--->

--->ImsConfigImplBase.java(继承IImsConfig.aild)-->ImsConfigImpl中的setFeatureValue(继承ImsConfigImplBase)-->ImsConfigStorage中的setFeatureValue

vendor\mediatek\proprietary\packages\services\Ims\src\com\mediatek\ims\config\internal\ImsConfigStorage.java

public void setFeatureValue(int featureId, int network, int value)
throws ImsException {
synchronized(mFeatureLock) {
mFeatureHelper.updateFeature(featureId, network, value);
}
} //当前类中的内部类 FeatureHelper
private static class FeatureHelper { private void updateFeature(int featureId, int network, int value) {
int curValue = -1;
boolean result = false;
ContentValues cv = new ContentValues();
cv.put(ImsConfigContract.Feature.PHONE_ID, mPhoneId);
cv.put(ImsConfigContract.Feature.FEATURE_ID, featureId);
cv.put(ImsConfigContract.Feature.NETWORK_ID, network);
cv.put(ImsConfigContract.Feature.VALUE, value); // Check exist or not
try {
curValue = getFeatureValue(featureId, network);
if (DEBUG) Log.d(TAG, "updateFeature() comparing: curValue: " +
curValue + ", value:" + value);
if (!checkIfBroadcastOnce(featureId, mPhoneId) || curValue != value || curValue == -1) {
mContentResolver.update(
ImsConfigContract.Feature.getUriWithFeatureId(mPhoneId, featureId, network),
cv, null, null);
}
} catch (ImsException e) {
Log.e(TAG, "updateFeature() ImsException featureId:" + featureId +", value:" + value);
mContentResolver.insert(ImsConfigContract.Feature.CONTENT_URI, cv);
}
} }

参考链接

IMS的注册流程分析

开关VoLTE流程分析(一)

Android8.1 开关VOLTE流程分析的更多相关文章

  1. 开关VoLTE流程分析(一)

    开关按钮位置: 设置--> 更多--> 移动网络--> 增强型4G LTE模式 控件初始化addEnhanced4GLteSwitchPreference,该设置开关使用了Switc ...

  2. 开关VoLTE流程分析(二)

    AT指令,暂未发现发送AT请求的frameworks接口,通过打印信息总结AT指令: AT+EIMSVOICE: Enable/Disable IMS Voice Capability+EIMSVOI ...

  3. Android8.1 SystemUI源码分析之 Notification流程

    代码流程 1.先看UI显示,StatuBar加载 CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域) SystemUI\src\ ...

  4. Android8.1 SystemUI源码分析之 电池时钟刷新

    SystemUI源码分析相关文章 Android8.1 SystemUI源码分析之 Notification流程 分析之前再贴一下 StatusBar 相关类图 电池图标刷新 从上篇的分析得到电池图标 ...

  5. Android7.0 Phone应用源码分析(一) phone拨号流程分析

    1.1 dialer拨号 拨号盘点击拨号DialpadFragment的onClick方法会被调用 public void onClick(View view) { int resId = view. ...

  6. Solr4.8.0源码分析(5)之查询流程分析总述

    Solr4.8.0源码分析(5)之查询流程分析总述 前面已经写到,solr查询是通过http发送命令,solr servlet接受并进行处理.所以solr的查询流程从SolrDispatchsFilt ...

  7. 8、Struts2 运行流程分析

    1.流程分析: 请求发送给 StrutsPrepareAndExecuteFilter StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 ...

  8. freeswitch呼叫流程分析

    今天翻文档时发现之前整理的关于freeswitch呼叫相关的内容,写成博文分享出来也方便我以后查阅. 整体结构图 FreeswitchCore 模块加载过程 freeswitch主程序初始化时会从mo ...

  9. u-boot 流程分析

    u-boot 介绍: 对于计算机来说 , 从一开始上机通电是无法直接启动操作系统的 , 这中间需要一个引导过程 , 嵌入式Linux系统同样离不开引导程序 ,  这个启动程序就叫启动加载程序(Boot ...

随机推荐

  1. 通过ADB获取Android手机信息

    原文:https://blog.csdn.net/fasfaf454/article/details/51438743 1.获取手机系统信息( CPU,厂商名称等)adb shell "ca ...

  2. 61-如何使用 Weave 网络?

    weave 是 Weaveworks 开发的容器网络解决方案.weave 创建的虚拟网络可以将部署在多个主机上的容器连接起来.对容器来说,weave 就像一个巨大的以太网交换机,所有容器都被接入这个交 ...

  3. Newifi D1或 D2在Openwrt中,启用硬件NAT,启用BBR

    Newifi D1或 D2在Openwrt中,启用硬件NAT,启用BBR 转载注明来源: 本文链接 来自osnosn的博客,写于 2019-09-27. 启用 mt7621的硬件nat (Newifi ...

  4. 我这样回答了Spring 5的新特性,面试官对我刮目相看

    最近,有一个小伙伴拿到了自己满意的Offer,和他交谈的过程中得知他面试官问他关于Spring的问题比较多,其中最让面试官满意的就是自己回答关于Spring 5的知识点回答的不错. Spring5于2 ...

  5. IT兄弟连 HTML5教程 CSS3属性特效 倒影

    在Web制作中,有些时候需要实现一些倒影的效果.在传统网页中,我们只能使用photoshop事先将倒影设计好,然后导入到网页中,这样不但耗费资源,也阻碍了开发效率.而CSS新增了Reflections ...

  6. 带你自行搭建虚拟机和Redis集群环境,值得收藏!

    前言: 我们看到分析 Redis 使用或原理的文章不少,但是完整搭建一套独立的 redis 集群环境的介绍,并不是很多或者说还不够详细. 那么,本文会手把手带着大家搭建一套 Redis 集群环境,Re ...

  7. `MediaDevices.getUserMedia` `undefined` 的问题

    通过 MediaDevices.getUserMedia() 获取用户多媒体权限时,需要注意其只工作于以下三种环境: localhost 域 开启了 HTTPS 的域 使用 file:/// 协议打开 ...

  8. How to: Change the Format Used for the FullAddress and FullName Properties 如何:更改用于FullAddress和FullName属性的格式

    There are FullAddress and FullName properties in the Address and Person business classes that are su ...

  9. Entity Framework Core Code First 项目实践

    Entity Framework Core Code First 实践 任何一种技术的出现都是为了解决一系列特定的问题,只有了解了技术所要解决的关键问题,才能理解它的真正用途,之后,才能在实践中用好它 ...

  10. 头条小视频和西瓜视频signature签名算法

    点击上方↑↑↑蓝字[协议分析与还原]关注我们 "分析今日头条内小视频和西瓜视频分享后浏览器打开所用的signature签名算法." 上月写的一篇关于使用微信的wxid加好友的文章, ...