Android7.0 数据业务长连接拨号过程

本文详细分析了Android 7.0中数据业务长连接拨号的框架流程,从CellDataPreference的点击事件开始,经过TelephonyManager、PhoneInterfaceManager、GsmCdmaPhone、DcTracker、DataConnection等组件,直至网络配置和状态通知。重点讨论了DataConnection的状态机以及ConnectivityService和NetworkManagementService在数据连接管理中的作用。
摘要由CSDN通过智能技术生成

前面我们已经分析了android在进行数据业务拨号前,进行相关准备工作的流程,现在我们可以分析一下整个数据业务长连接拨号在框架部分的流程。

长连接的“长”,是相对于终端进行彩信发送等操作时,建立的临时数据连接而言的(这种临时数据连接在业务执行完毕后,会主动断开),是能够长时间存在的数据连接。

1 CellDataPreference
我们从点击UI界面的数据拨号开关开始分析整个流程。
在原生的Android代码中,数据开关作为设置的一部分,相关的操作定义于CellDataPreference.java中,定义于packages/apps/settings/src/com/android/settings/datausage目录下。

我们看看处理点击操作的performClick函数:

@Override
protected void performClick(View view) {
	.............
	//开关处于开启状态
	if (mChecked) {
		//当前subId对应的卡信息(卡需要处于激活状态,即相关信息已经加载)
		final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
		//默认数据卡对应的卡状态
		final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();

		//showSimCardTile判断手机是否支持多卡,支持的话返回true
		//整个If的含义就是:仅支持单卡,或者默认数据卡与当前的卡信息一致
		if (!Utils.showSimCardTile(getContext()) || (nextSir != null && currentSir != null &&
		currentSir.getSubscriptionId() == nextSir.getSubscriptionId())) {
			//关闭数据业务(开关处于开启态,再点击一次,变成关闭态)
			setMobileDataEnabled(false);
			if (nextSir != null && currentSir != null &&
			currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
				//双卡的情况下,还要关闭另一张卡的数据业务(当前卡为默认数据卡,这里是以防万一)
				disableDataForOtherSubscriptions(mSubId);
			}
			return;
		}
		............
		super.performClick(view);
	} else {
		//这里是从关到开的过程,多卡的情况
		if (Utils.showSimCardTile(getContext())) {
			//将标志位置为true
			mMultiSimDialog = true;
			//调用父类方法;在父类方法中最终将调用子类实现的onClick方法
			super.performClick(view);
		} else {
			//单卡时直接开始拨号
			setMobileDataEnabled(true);
		}
	}
}

从上面的代码我们可以看出,在多卡的情况下,将开关从关闭置为打开,将由CellDataPreference的onClick函数进行处理:

@Override
protected void onClick(DialogInterface dialog, int which) {
	if (which != DialogInterface.BUTTON_POSITIVE) {
		return;
	}

	//在前面的代码中,mMultiSimDialog已经置为true,表示手机支持多卡
	if (mMultiSimDialog) {
		//将当前CellDataPreference对应卡设为默认数据卡
		mSubscriptionManager.setDefaultDataSubId(mSubId);
		//开始数据拨号
		setMobileDataEnabled(true);
		//关闭另一张卡的数据业务
		disableDataForOtherSubscriptions(mSubId);
	} else {
		// TODO: extend to modify policy enabled flag.
		setMobileDataEnabled(false);
	}
}

private void setMobileDataEnabled(boolean enabled) {
	if (DataUsageSummary.LOGD) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," + mSubId + ")");
	//调用TelephonyManager的接口
	mTelephonyManager.setDataEnabled(mSubId, enabled);
	//更改界面
	setChecked(enabled);
}

2 TelephonyManager
根据上文的代码,我们知道设置界面最终通过调用TelephonyManager开启拨号流程。

//传入参数subId为数据卡对应的subId
//enable为true表示开启数据业务;false表示关闭数据业务
@SystemApi
public void setDataEnabled(int subId, boolean enable) {
	try {
		Log.d(TAG, "setDataEnabled: enabled=" + enable);
		//获取binder代理对象
		ITelephony telephony = getITelephony();
		if (telephony != null)
			//通过binder通信调用接口
			telephony.setDataEnabled(subId, enable);
	} catch (RemoteException e) {
		Log.e(TAG, "Error calling ITelephony#setDataEnabled", e);
	}
}

private ITelephony getITelephony() {
	//Context.TELEPHONY_SERVICE对应字符串"phone"
	return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}

上面的代码较为简单,唯一值得关注的是找到binder通信对应的服务提供者。
实际上我们在之前的blog中已经提到过了,在PhoneApp启动时会创建PhoneGlobals,而PhoneGlobals会创建PhoneInterfaceManager:

3 PhoneInterfaceManager

.......
phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());
......

我们来看看PhoneInterfaceManager的定义:

//可以看到PhoneInterfaceManager继承ITelephony.Stub,与前面呼应起来了
public class PhoneInterfaceManager extends ITelephony.Stub {
	.............
	static PhoneInterfaceManager init(PhoneGlobals app, Phone phone) {
        synchronized (PhoneInterfaceManager.class) {
            if (sInstance == null) {
	            //创建PhoneInterfaceManager
                sInstance = new PhoneInterfaceManager(app, phone);
            } else {
                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
            }
            return sInstance;
        }
	}

	private PhoneInterfaceManager(PhoneGlobals app, Phone phone) {
		...........
		publish();
	}

	private void publish() {
        if (DBG) log("publish: " + this);
		//publish服务名为"phone",与前面对应起来了
        ServiceManager.addService("phone", this);
    }
    ...........
}

至此,我们知道了TelephonyManager通过Binder通信调用的实际上是PhoneInterfaceManager中的接口。

@Override
public void setDataEnabled(int subId, boolean enable) {
	//检查权限
	enforceModifyPermission();
	//利用subId映射得到phoneId
	int phoneId = mSubscriptionController.getPhoneId(subId);
	if (DBG) log("getDataEnabled: subId=" + subId + " phoneId=" + phoneId);
	//根据phoneId得到PhoneFactory中记录的phone对象
	Phone phone = PhoneFactory.getPhone(phoneId);
	if (phone != null) {
		if (DBG) log("setDataEnabled: subId=" + subId + " enable=" + enable);
		//调用phone对象的setDataEnabled方法
		phone.setDataEnabled(enable);
	} else {
		loge("setDataEnabled: no phone for subId=" + subId);
	}
}

4 GsmCdmaPhone
与之前的版本不一样,Android 7.0中新增了GsmCdmaPhone对象,并且将Phone变成了一个抽象类,新增了PhoneInternaInterface接口:

public abstract class Phone extends Handler implements PhoneInternalInterface {
	...........
}
public class GsmCdmaPhone extends Phone {
	...........
}

这种继承结构的变化带来的影响,自己目前也还没有深入研究,今后有流程涉及时,再做进一步的分析。
我们目前还是关注数据拨号流程:

@Override
public void setDataEnabled(boolean enable) {
	mDcTracker.setDataEnabled(enable);
}

可以看到在拨号流程中,GsmCdmaPhone的工作很简单,直接调用DcTracker的接口即可。

5 DcTracker
在Android 7.0中,去掉了DcTrackerBase对象,DcTracker直接继承Handler。

public class DcTracker extends Handler {
	.......
	public void setDataEnabled(boolean enable) {
		Message msg = obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE);
        msg.arg1 = enable ? 1 : 0;
        if (DBG) log("setDataEnabled: sendMessage: enable=" + enable);
        //发送消息给自己,将调用onSetUserDataEnabled进行处理
        sendMessage(msg);
    }
    ........
    private void onSetUserDataEnabled(boolean enabled) {
        synchronized (mDataEnabledLock) {
	        //新设定的状态,与旧状态不一样时,才需要继续处理
            if (mUserDataEnabled != enabled) {
                mUserDataEnabled = enabled;

                // 更新数据库,注意到单、双卡更新字段的区别
                if (TelephonyManager.getDefault().getSimCount() == 1) {
                    Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
                } else {
                    int phoneSubId = mPhone.getSubId();
                    Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA + phoneSubId,
                            enabled ? 1 : 0);
                }
                //根据系统属性判断终端是否允许在漫游状态使用数据业务
                if (getDataOnRoamingEnabled() == false &&
                        mPhone.getServiceState().getDataRoaming() == true) {
                    if (enabled) {
	                    //仅为不可用的APN发送通知
                        notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
                    } else {
                        notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
                    }
                }

                if (enabled) {
	                //开启数据业务时,调用该函数
                    onTrySetupData(Phone.REASON_DATA_ENABLED);
                } else {
	                //关闭数据业务时,调用该函数
                    onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
                }
            }
        }
    }
	..............
}

onTrySetupData的内容较为简单,直接调用setupDataOnConnectableApns:

private boolean onTrySetupData(String reason) {
	if (DBG) log("onTrySetupData: reason=" + reason);
	//顾名思义,将利用可连接的APN进行拨号
	setupDataOnConnectableApns(reason);
	return true;
}

private void setupDataOnConnectableApns(String reason) {
	//这里RetryFailures.ALWAYS表示连网失败话,会一直重试
	setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
}

private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
	...............
	//介绍Phone拨号前的准备工作时,我们已经已经mPrioritySortedApnContexts是通过解析xml文件形成的
	for (ApnContext apnContext : mPrioritySortedApnContexts) {
		//如果apnContext之前用过,不处于Idle态(apnContext初始时处于Idle态),那么按需释放对应的数据连接,这一部分我们目前不用太关注
		......................
		//注意到apnContext的isConnectable返回true时,拨号流程才能继续下去
		if (apnContext.isConnectable()) {
			log("isConnectable() call trySetupData");
			apnContext.setReason(reason);
			//首次使用的ApnContext, waitingApns为null
			trySetupData(apnContext, waitingApns);
		}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值