zoukankan      html  css  js  c++  java
  • Android8.1 开关VOLTE流程分析

    前言

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

    开始

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

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

    源码位置 vendormediatekproprietarypackagesappsMtkSettingssrccomandroidsettings etworkNetworkDashboardFragment.java

    NetworkDashboardFragment加载的布局xml为 network_and_internet.xml

    源码位置 vendormediatekproprietarypackagesappsMtkSettings esxml etwork_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

    源码位置 vendormediatekproprietarypackagesservicesTelephonysrccomandroidphoneMobileNetworkSettings.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的打开和关闭。

    继续跟进 源码位置 vendormediatekproprietaryframeworksopt etimssrcjavacommediatekimsinternalMtkImsManager.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()结果

    源码位置 frameworksopt etimssrcjavacomandroidimsImsManager.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 源码位置 frameworksase elephonyjavaandroid elephonyCarrierConfigManager.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中,源码位置 frameworksopt etimssrcjavacomandroidimsImsManager.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()

    源码位置 frameworksase elephonyjavacomandroidimsinternalIImsService.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()保存值

    源码位置 frameworksase elephonyjavacomandroidimsImsConfig.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

    vendormediatekproprietarypackagesservicesImssrccommediatekimsconfiginternalImsConfigStorage.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流程分析(一)

  • 相关阅读:
    Django安装与创建项目
    siege 高并发测试工具
    http_load 高并发测试
    webbench高并发测试
    scss切页面
    切页面
    小程序scss页面布局
    rtrim
    modal结合art-template
    Python 正则表达式
  • 原文地址:https://www.cnblogs.com/cczheng-666/p/10846190.html
Copyright © 2011-2022 走看看