最近有需求須要實現插卡默認打開Volte功能,順帶研究了下Volte的流程,在此作個記錄html
從Settings設置界面入手,網絡和互聯網-->移動網絡-->VoLTE高清通話(電信卡)/加強型4G LTE模式(移動卡)java
找到網絡和互聯網加載對應的Fragment爲NetworkDashboardFragment,android
源碼位置 vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\network\NetworkDashboardFragment.java,網絡
NetworkDashboardFragment加載的佈局xml爲 network_and_internet.xmlapp
源碼位置 vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\network_and_internet.xmlide
<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多行,別被嚇到了,只需找咱們關心的便可ui
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開關的點擊事件this
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); } } }