Android8.1 开关VOLTE流程分析
前言
最近有需求需要实现插卡默认打开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); } } }