前言
最近有需求需要實現插卡預設開啟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);
}
}
}