欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android8.1 开关VOLTE流程分析

程序员文章站 2022-05-22 13:29:02
前言 最近有需求需要实现插卡默认打开Volte功能,顺带研究了下Volte的流程,在此做个记录 开始 从Settings设置界面入手,网络和互联网 移动网络 VoLTE高清通话(电信卡)/增强型4G LTE模式(移动卡) 找到网络和互联网加载对应的Fragment为NetworkDashboardF ......

前言

最近有需求需要实现插卡默认打开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流程分析(一)