----该阶段是指,在DataConnection收到DcTracker的请求之后,将请求转交给RILJ的过程,经过该阶段后,请求就发送到了RIL以及Modem层,由底层完成信令的发送和接收;
//漫游相关 static final String REASON_ROAMING_ON = "roamingOn"; static final String REASON_ROAMING_OFF = "roamingOff"; //PS attach static final String REASON_DATA_ATTACHED = "dataAttached"; //APN改变 static final String REASON_APN_CHANGED = "apnChanged"; //通话结束 static final String REASON_VOICE_CALL_ENDED = "2GVoiceCallEnded"; //SIM载入完毕 static final String REASON_SIM_LOADED = "simLoaded"; //网络模式改变 static final String REASON_NW_TYPE_CHANGED = "nwTypeChanged";我们挑选最常见的REASON_DATA_ATTACHED原因来分析网络连接的发起过程。
protected void registerForAllEvents() { //监听是否PS域Attach状态 mPhone.getServiceStateTracker().registerForDataConnectionAttached(this, DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null); }因此当PS域Attach成功之后,将会收到EVENT_DATA_CONNECTION_ATTACHED消息:
public void handleMessage (Message msg) { switch (msg.what) { case DctConstants.EVENT_DATA_CONNECTION_ATTACHED: onDataConnectionAttached(); break; } }继续:
private void onDataConnectionAttached() { mAttached.set(true); if (getOverallState() == DctConstants.State.CONNECTED) { //已经处于连接状态 startNetStatPoll(); startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); notifyDataConnection(Phone.REASON_DATA_ATTACHED); } else { //初始化后应该处于IDLE状态 notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED); } if (mAutoAttachOnCreationConfig) { mAutoAttachOnCreation = true; } setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED); }当前的连接状态是IDLE,因此会将REASON_DATA_ATTACHED的参数传递给notifyOffApnsOfAvailability(),里面也没什么内容,我们直接来看setupDataOnConnectableApns()的操作。
private void setupDataOnConnectableApns(String reason) { //对于当前环境来说,reason为REASON_DATA_ATTACHED for (ApnContext apnContext : mPrioritySortedApnContexts) { if (apnContext.getState() == DctConstants.State.FAILED) { apnContext.setState(DctConstants.State.IDLE); } if (apnContext.isConnectable()) { apnContext.setReason(reason); trySetupData(apnContext); } } }在这个方法里面,遍历当前所有的APN参数(mPrioritySortedApnContexts其实等同于mApnContexts,只不过是按照优先级顺序排列了一下而已,项都是一样的),找到可用的APN参数,然后通过trySetupData()发起连接请求,我们来看一下这里如何判断一个APN是否可用,也就是看一下isConnectable()的判断条件:
@ApnContext.java public boolean isConnectable() { return isReady() && ((mState == DctConstants.State.IDLE) || (mState == DctConstants.State.SCANNING) || (mState == DctConstants.State.RETRYING) || (mState == DctConstants.State.FAILED)); }前面我们说过, 激活APN的时候,其实就是让isReady()的判断通过,而mState初始值也是IDLE,因此刚才我们激活的APN参数此时就被挑选出来,并进行trySetupData()的操作:
@DcTracker.java private boolean trySetupData(ApnContext apnContext) { boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY); boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState(); boolean checkUserDataEnabled = !(apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)); if (apnContext.isConnectable() && (isEmergencyApn || (isDataAllowed(apnContext) && getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()))) { int radioTech = mPhone.getServiceState().getRilDataRadioTechnology(); if (apnContext.getState() == DctConstants.State.IDLE) { ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech); if (waitingApns.isEmpty()) { notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext); notifyOffApnsOfAvailability(apnContext.getReason()); return false; } else { apnContext.setWaitingApns(waitingApns); } } //建立连接 boolean retValue = setupData(apnContext, radioTech); notifyOffApnsOfAvailability(apnContext.getReason()); return retValue; } else { } }这里主要经历了有效性的检查,其中判断了四个情况:
public boolean getAnyDataEnabled(boolean checkUserDataEnabled) { synchronized (mDataEnabledLock) { if (!(mInternalDataEnabled && (!checkUserDataEnabled || mUserDataEnabled) && (!checkUserDataEnabled || sPolicyDataEnabled))) return false; for (ApnContext apnContext : mApnContexts.values()) { if (isDataAllowed(apnContext)) { return true; } } return false; } }这里的判断中最重要的就是mUserDataEnabled(),他的来源:
mUserDataEnabled = Settings.Global.getInt( mPhone.getContext().getContentResolver(), Settings.Global.MOBILE_DATA, 1) == 1;这说明他来自于Settings.Global.MOBILE_DATA这个属性值,而这个属性值恰恰就是当用户打开或关闭移动网络时所改变的属性值, 当用户打开数据网络时,该值为1,关闭网络时,该值就是0。
private boolean setupData(ApnContext apnContext, int radioTech) { ApnSetting apnSetting; DcAsyncChannel dcac = null; apnSetting = apnContext.getNextWaitingApn(); int profileId = apnSetting.profileId; if (profileId == 0) { profileId = getApnProfileID(apnContext.getApnType()); } if (dcac == null) { //创建DcAsyncChannel dcac = findFreeDataConnection(); if (dcac == null) { dcac = createDataConnection(); } if (dcac == null) { return false; } } apnContext.setDataConnectionAc(dcac); apnContext.setApnSetting(apnSetting); apnContext.setState(DctConstants.State.CONNECTING); mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); //通过DcAsyncChannel发起连接请求 Message msg = obtainMessage(); msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE; msg.obj = apnContext; dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation, msg); return true; }这个方法内部主要完成了两个任务:
private DcAsyncChannel createDataConnection() { int id = mUniqueIdGenerator.getAndIncrement(); DataConnection conn = DataConnection.makeDataConnection(mPhone, id, this, mDcTesterFailBringUpAll, mDcc); mDataConnections.put(id, conn); //创建DcAsyncChannel通道 DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG); //申请双向连接 int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler()); if (status == AsyncChannel.STATUS_SUCCESSFUL) { mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac); } else { } return dcac; }这里我们主要完成四个步骤:
public class DcAsyncChannel extends AsyncChannel {}根据《 AsyncChannel的使用和原理》中的介绍,通过fullyConnectSync()可以一次性申请到双向的AsyncChannel,对于当前环境来说,就是 在DcTracker与DataConnection之间建立了双向的连接通道,而且把通道的地址传递出来,而在setupData()中我们看到,DcTracker拿到该通道后,就进行了bringUp()的调用;
private boolean setupData(ApnContext apnContext, int radioTech) { DcAsyncChannel dcac = null; //通过DcAsyncChannel发起连接请求 Message msg = obtainMessage(); msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE; msg.obj = apnContext; dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation, msg); return true; }而且在调用bringUp时传递了一个Message的参数,该参数中携带了两个参数,EVENT_DATA_SETUP_COMPLETE的消息和APN的内容。
@DcAsyncChannel.java public void bringUp(ApnContext apnContext, int initialMaxRetry, int profileId, int rilRadioTechnology, boolean retryWhenSSChange, Message onCompletedMsg) { sendMessage(DataConnection.EVENT_CONNECT, new ConnectionParams(apnContext, initialMaxRetry, profileId, rilRadioTechnology, retryWhenSSChange, onCompletedMsg)); }这里就是将刚才的两个参数封装后通过sendMessage()发送出去,那么这个消息是发送给谁了呢?
其实就是发送给DcAsyncChannel通道的另一端DataConnection了,那么DataConnection是如何处理该消息的呢?
@DataConnection.java static DataConnection makeDataConnection(PhoneBase phone, int id, DcTrackerBase dct, DcTesterFailBringUpAll failBringUpAll, DcController dcc) { //创建DataConnection方法 DataConnection dc = new DataConnection(phone, "DC-" + mInstanceNumber.incrementAndGet(), id, dct, failBringUpAll, dcc); dc.start(); return dc; }先来看该类的属性:
public final class DataConnection extends StateMachine {}然后来看该对象的构造方法:
private DataConnection(PhoneBase phone, String name, int id, DcTrackerBase dct, DcTesterFailBringUpAll failBringUpAll, DcController dcc) { super(name, dcc.getHandler()); mPhone = phone; mDct = dct; mDcTesterFailBringUpAll = failBringUpAll; mDcController = dcc; mId = id; mCid = -1; mDcRetryAlarmController = new DcRetryAlarmController(mPhone, this); ServiceState ss = mPhone.getServiceState(); mRilRat = ss.getRilDataRadioTechnology(); mDataRegState = mPhone.getServiceState().getDataRegState(); int networkType = ss.getDataNetworkType(); mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, networkType, NETWORK_TYPE, TelephonyManager.getNetworkTypeName(networkType)); mNetworkInfo.setRoaming(ss.getRoaming()); mNetworkInfo.setIsAvailable(true); //各种状态机初始化 addState(mDefaultState); addState(mInactiveState, mDefaultState); addState(mActivatingState, mDefaultState); addState(mRetryingState, mDefaultState); addState(mActiveState, mDefaultState); addState(mDisconnectingState, mDefaultState); addState(mDisconnectingErrorCreatingConnection, mDefaultState); //默认状态为DcInactiveState setInitialState(mInactiveState); mApnContexts = new ArrayList<ApnContext>(); }从他的属性和构造方法可以看到,该类其实是一个状态机,内部定义了七种状态,其中默认状态为DcInactiveState。这些状态分别代表了一个连接从非激活状态到激活状态再到断开状态所经历的过程。
private class DcInactiveState extends State { public boolean processMessage(Message msg) { switch (msg.what) { case EVENT_CONNECT: ConnectionParams cp = (ConnectionParams) msg.obj; //初始化连接环境 if (initConnection(cp)) { //发起连接请求 onConnect(mConnectionParams); //进入正在激活状态 transitionTo(mActivatingState); } else { notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER, false); } retVal = HANDLED; break; } return retVal; } }在这个方法里面我们看到,其先将EVENT_CONNECT中打包的参数在initConnection中解压出来,然后就通过onConnect()方法发起连接请求,再然后就进入DcActivatingState的状态。
private void onConnect(ConnectionParams cp) { //向Modem注册的回调消息 Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp); msg.obj = cp; //向RIL发起连接请求 mPhone.mCi.setupDataCall( Integer.toString(cp.mRilRat + 2), Integer.toString(cp.mProfileId), mApnSetting.apn, mApnSetting.user, mApnSetting.password, Integer.toString(authType), protocol, msg); }到这里我们终于看到与Modem的交互了,其实发起数据连接的最终都是通过RILJ的setupDataCall的接口来实现的,该接口传递了一些必要的连接参数,包括:当前的接入技术、APN的优先级、APN的参数、以及回调消息。