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

原创 2016年09月01日 16:29:51

前面我们已经分析了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);
        }
    }
}

我们看看ApnContext中的isConnectable等函数:

public boolean isConnectable() {
    return isReady() && ((mState == DctConstants.State.IDLE)
                            || (mState == DctConstants.State.SCANNING)
                            || (mState == DctConstants.State.RETRYING)
                            || (mState == DctConstants.State.FAILED));
}

public boolean isReady() {
    //ApnContext被激活时,mDataEnabled才会变为true;mDependencyMet从配置中读出来,衡为true
    return mDataEnabled.get() && mDependencyMet.get();
}

根据前面的数据业务拨号准备工作的流程,我们知道ConnectivityService中default NetworkRequest对应类型的ApnContext被激活了,也就是Default Type的APN被激活了。
因此,我们至少有一个connectable的APN可以使用, 可以继续拨号流程。

trySetupData函数中主要根据当前终端的运行状态,判断框架是否应该继续拨号。

private boolean trySetupData(ApnContext apnContext, ArrayList<ApnSetting> waitingApns) {
    //判断拨号条件
    ..........
    //整个判断能否拨号的过程比较复杂,涉及了很多细节
    //本来打算详细写一下,但写了一部分后,发现太乱了
    //这里就大概描述一下:其实这里就是检查之前的准备工作是否完成,主要是结合APN类型、数据能力是否激活及数据开关是否打开等,判断处能否继续拨号
    if (......../*满足拨号条件*/) {
        ............
        if (apnContext.getState() == DctConstants.State.IDLE) {
            if (waitingApns == null) {
                //结合激活的apnContext的type,例如default类型,以及底层使用的无线技术(从servicestate获取),从Dctracker加载卡对应的所有apn中,得到可以使用的Apn
                waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
            }
            if (waitingApns.isEmpty()) {
                //没获取到可用apn
                ........
                return false;
            } else {
                //将所有可用的apn存入apnContext
                apnContext.setWaitingApns(waitingApns);
                .............
            }
        }
        //继续拨号
        boolean retValue = setupData(apnContext, radioTech);
        ...........
        return retValue;
    } else {
        //打印不允许拨号的原因
        .................
    }
}

之前写blog总想尽可能的详细,以便以后查阅,但这次真是被trySetupData打败了。回过头来想想,自己之前的想法可能确实是有问题的,对于整个Framework而言,重要的是架构和主要流程的脉络,自己过于注重细节,反而影响阅读和记录的效率。可能自己需要分析Framework的bug,因此很多时候不得不关注细节,于是产生了现在的毛病,以后行文风格要力求简洁。

现在,我们回过头来看看setupData:

private boolean setupData(ApnContext apnContext, int radioTech) {
    ..........
    //用于连接DcTracker和DataConnection(后文描述)
    DcAsyncChannel dcac = null;

    //从apncontext取出可用的apn
    apnSetting = apnContext.getNextApnSetting();
    .............
    //得到profileId,这个需要传递给modem
    int profileId = apnSetting.profileId;
    if (profileId == 0) {
        profileId = getApnProfileID(apnContext.getApnType());
    }
    //不同类型Apn的profileId定义于RILConstants中
    //例如:
    //public static final int DATA_PROFILE_DEFAULT   = 0;
    //public static final int DATA_PROFILE_TETHERED  = 1;
    //public static final int DATA_PROFILE_IMS       = 2;

    if (dcac == null) {
        //根据无线技术,判断是否只允许建立一个DataConnection
        //无线技术能否支持多个数据连接,由frameworks/base/core/res/res/values/config.xml决定
        if (isOnlySingleDcAllowed(radioTech)) {
            //当无线技术仅支持单连接时,若有高优先级的APN被激活,那么此次拨号无法继续
            //举例来说,就是某些无线技术下,彩信发送时,default数据不能拨号
            //目前原生中,仅支持单连接的无线技术为IS95A,IS95B,1xRTT,EVDO_0,EVDO_A,EVDO_B
            if (isHigherPriorityApnContextActive(apnContext)) {
                return false;
            }

            //在仅支持单连接的情况下,拨号前需要清楚所有已建立的连接
            //代码走到这里,说明当前APN的优先级是最高的,需要清除低优先级的连接
            //举例来说,在上述无线技术下,建立彩信时,会断开已连接的default数据业务;当然彩信发送完毕后,会自动重新建立default的数据连接
            if (cleanUpAllConnections(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
                return false;
            }
        }
        //判断能否复用dataConnection
        dcac = findFreeDataConnection();

        if (dcac == null) {
            //不能复用则创建新的dataConnection和dcAsyncChannel
            dcac = createDataConnection();
        }
        ..........
    }
    //下面均是更新ApnContext的状态
    final int generation = apnContext.incAndGetConnectionGeneration();
    .............
    apnContext.setDataConnectionAc(dcac);
    apnContext.setApnSetting(apnSetting);
    apnContext.setState(DctConstants.State.CONNECTING);

    Message msg = obtainMessage();
    //注意此msg类型;当dataConnection拨号成功后,将会返回此消息给dcTracker
    msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
    msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
    //调用dcAsyncChannel的bringUp
    dcac.bringUp(apnContext, profileId, radioTech, msg, generation);

    if (DBG) log("setupData: initing!");
    return true;
}

根据上面的代码,我们知道了当DcTracker判断出拨号的准备工作OK时,将创建出DataConnection对象,然后调用DcAsyncChannel的bringUp函数。

6 DataConnection

6.1 创建过程
我们先看看DcTracker中创建DataConnection的过程:

private DcAsyncChannel createDataConnection() {
    ..........
    //每个DataConnection有唯一的id号
    int id = mUniqueIdGenerator.getAndIncrement();
    //创建dataconnection,注意this为dctracker
    DataConnection conn = DataConnection.makeDataConnection(mPhone, id, this, mDcTesterFailBringUpAll, mDcc);
    //dctracker保存dataconnection
    mDataConnections.put(id, conn);
    //创建DcAsyncChannel
    DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
    //其中调用AsyncChannel中的方法,完成dctracker与dataconnection之间handler的绑定;如同connectivityService与NetworkFactory的绑定一样
    int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
    if (status == AsyncChannel.STATUS_SUCCESSFUL) {
        //dctracker保存DcAsyncChannel,键值为对应dataConnection的id
        mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac);
    } else {
        .........
    }

    return dcac;
}

现在我们看看makeDataConnection方法:

public static DataConnection makeDataConnection(.....) {
    DataConnection dc = new DataConnection(phone,
                "DC-" + mInstanceNumber.incrementAndGet(), id, dct, failBringUpAll, dcc);
    //DataConnection是个状态机,因此需要start
    dc.start();
    return dc;
}

最后,看看DataConnection的构造函数:

//继承状态机
public class DataConnection extends StateMachine {
    ......
    private DataConnection(......) {
        ...........
        //拨号成功后,将利用NetworkInfo构造NetworkAgent,注册到ConnectivityService
        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
                networkType, NETWORK_TYPE, TelephonyManager.getNetworkTypeName(networkType));
        ...........
        //调用状态机中的addState方法
        addState(mDefaultState);
            //后面的状态是前面的父状态
            addState(mInactiveState, mDefaultState);
            addState(mActivatingState, mDefaultState);
            addState(mActiveState, mDefaultState);
            addState(mDisconnectingState, mDefaultState);
            addState(mDisconnectingErrorCreatingConnection, mDefaultState);
        //初始态为InactiveState
        setInitialState(mInactiveState);
        ...........
    }
}

DataConnection是一个状态机,android中状态机的实现原理,以后再单独分析。这里我们只需要知道,状态机内部有自己的Handler,收到消息时由当前状态进行处理;若当前状态无法处理,则递交给父状态进行处理。当从一个状态离开时,将调用该状态的exit函数(可以是空实现);当进入到一个状态时,将调用该状态的enter函数(可以是空实现)。

6.2 DataConnection拨号
前文已经描述,在DcTracker创建完DataConnection和DcAysncChannel后,调用了DcAsyncChannel的bringUp函数:

public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology,
                  Message onCompletedMsg, int connectionGeneration) {
    //调用AsyncChannel的sendMessage方法,将消息发送给dstMessenger,也就是dataConnection
    sendMessage(DataConnection.EVENT_CONNECT,
            new ConnectionParams(apnContext, profileId, rilRadioTechnology,
                onCompletedMsg, connectionGeneration));
}

6.2.1 DcInactiveState
根据DataConnection的构造函数,我们知道DataConnection初始时处于DcInactiveState,于是应该由DcInactiveState处理EVENT_CONNECT事件:

private class DcInactiveState extends State {
    .........
    @Override
    public boolean processMessage(Message msg) {
        .........
        switch (msg.what) {
            ...........
            case EVENT_CONNECT:
                ConnectionParams cp = (ConnectionParams) msg.obj;
                //判断参数的有效性
                if (initConnection(cp)) {
                    //进行实际的拨号操作
                    onConnect(mConnectionParams);
                    //dataConnection迁移到Activating状态
                    transitionTo(mActivatingState);
                } else {
                    //通知DcTracker拨号失败
                    notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER, false);
                }
                break;
        }
    }
}

我们看看onConnect函数:

private void onConnect(ConnectionParams cp) {
    ...........
    //拨号返回时的消息为EVENT_SETUP_DATA_CONNECTION_DONE
    Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
    msg.obj = cp;
    ...........
    //通过RIL将消息发送给modem
    mPhone.mCi.setupDataCall(
            cp.mRilRat,
            cp.mProfileId,
            mApnSetting.apn, mApnSetting.user, mApnSetting.password,
            authType,
            protocol, msg);
}

6.2.2 DcActivatingState
根据前面的代码,我们知道DataConnection在DcInactiveState状态,利用RIL向modem发送拨号请求后,进入到了DcActivatingState;同时,RIL收到modem拨号返回的消息后,将向DataConnection发送EVENT_SETUP_DATA_CONNECTION_DONE的消息。

根据状态机的原理,我们看看DcActivatingState处理消息的代码:

private class DcActivatingState extends State {
    @Override
    public boolean processMessage(Message msg) {
        .........
        switch (msg.what) {
            ...........
            case EVENT_SETUP_DATA_CONNECTION_DONE:
                ar = (AsyncResult) msg.obj;
                cp = (ConnectionParams) ar.userObj;
                //从RIL返回的msg中取出拨号的结果;当结果正常时,内部还调用了updateLinkProperty获取了链路信息
                DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
                ........
                switch (result) {
                    case SUCCESS:
                        mDcFailCause = DcFailCause.NONE;
                        transitionTo(mActiveState);
                        break;
                    //拨号出现异常时,将根据结果判断是否需要重新拨号,还是打印log,停止拨号
                    ..............
                }
                retVal = HANDLED;
                break;
            ................
        }
    }
}

从上面的代码可以看出,当底层返回拨号成功的消息后,DataConnection将进入到DcActiveState。

6.2.3 DcActiveState
DcActiveState实现了自己的enter函数,因此从DcActivatingState迁入时,首先将调用该enter函数:

private class DcActiveState extends State {
    @Override public void enter() {
        boolean createNetworkAgent = true;
        //如果队列中有断开连接的消息待处理,则不创建NetworkAgent
        if (hasMessages(EVENT_DISCONNECT) ||
                hasMessages(EVENT_DISCONNECT_ALL) ||
                hasDeferredMessages(EVENT_DISCONNECT) ||
                hasDeferredMessages(EVENT_DISCONNECT_ALL)) {
            log("DcActiveState: skipping notifyAllOfConnected()");
            createNetworkAgent = false;
        } else {
            //通知DcTracker拨号完成,消息为EVENT_DATA_SETUP_COMPLETE
            notifyAllOfConnected(Phone.REASON_CONNECTED);
        }
        //注册监听通话的开始和结束;由于通信制式的约束,同一个phone通话时必须断开数据业务,通话结束后,再重新连接        
        mPhone.getCallTracker().registerForVoiceCallStarted(getHandler(),
               DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED, null);
        mPhone.getCallTracker().registerForVoiceCallEnded(getHandler(),
               DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED, null);
        //更新NetworkInfo等
        ............
        if (createNetworkAgent) {
            //创建NetworkAgent,将注册到ConnectivityService
            mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
                    "DcNetworkAgent", mNetworkInfo, makeNetworkCapabilities(), mLinkProperties,
                    50, misc);
            }
    }
    .........
}

至此,与modem交互的拨号主要流程已经结束。然而,还有两件重要的事情没有做:1、通知其它APK,例如SystemUI,拨号成功;2、配置路由等,让终端可以真正的访问网络。
其中,第一件事是通过上述代码中的notifyAllOfConnected完成的,第二件事是通过创建DcNetworkAgent完成,接下来我们分别介绍完成这两件事的过程。

7 通知数据拨号成功
在DataConnection的DcActiveState中,我们已经知道拨号成功后,将调用notifyAllOfConnected函数:

private void notifyAllOfConnected(String reason) {
    notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE, reason);
}

private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
    ............
    for (ConnectionParams cp : mApnContexts.values()) {
        ..........
        //消息发送给了DcTracker
        Message msg = mDct.obtainMessage(event, pair);
        AsyncResult.forMessage(msg);
        msg.sendToTarget();
    }
}

DcTracker收到EVENT_DATA_SETUP_COMPLETE消息后,将调用onDataSetupComplete进行处理。

private void onDataSetupComplete(AsyncResult ar) {
    .....
    if (ar.exception == null) {
        ............
        if (dcac == null) {
            //正常情况下,拨号前创建过DcAsyncChannel,不会进入该分支
            .........
        } else {
            ApnSetting apn = apnContext.getApnSetting();
            //有些APN,利用mms用的,配置了Proxy等属性
            if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
                try {
                    String port = apn.port;
                    if (TextUtils.isEmpty(port)) port = "8080";
                    ProxyInfo proxy = new ProxyInfo(apn.proxy,
                            Integer.parseInt(port), null);
                    //通过DcAsyncChannel设入DataConnection的LinkProperties属性中
                    dcac.setLinkPropertiesHttpProxySync(proxy);
                } catch (NumberFormatException e) {
                    ......
                }
                .........
                //更新ApnContext的状态
                apnContext.setState(DctConstants.State.CONNECTED);

                //判断APN是否为网络端配置的,国内见的比较少
                boolean isProvApn = apnContext.isProvisioningApn();
                ..........
                //如果不是网络端配置的APN
                if ((!isProvApn) || mIsProvisioning) {
                    .........
                    completeConnection(apnContext);
                } else {
                    //网络配置的APN,进行通知后,关闭radio
                    //这里为什么要这么做,自己还不是太清楚
                    .........
                    setRadio(false);
                }
            }
        }
    } else {
        //拨号失败,也会发送通知
        .........
    }
    ......
}

通过上面的代码,我们知道DcTracker判断拨号结果符合要求后,将调用completeConnection函数:

private void completeConnection(ApnContext apnContext) {
    ........
    //进行通知工作
    mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
    //周期性读取底层接口文件,判断终端是否发送和接受数据,从而更新UI界面的上下行图标,以后单独介绍
    startNetStatPoll();
    //周期性地检测终端是否出现问题:同样是读取底层文件,当连续发送10个包,但没有收到回复时,认为终端出现问题,需要进行恢复,以后单独介绍
    startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
}

这里我们主要关注Phone对象的notifyDataConnection函数,这个函数现在由抽象类Phone来实现:

public void notifyDataConnection(String reason, String apnType) {
    mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType));
}

其中,mNotifier的类型为DefaultPhoneNotifier,是PhoneFactory调用makeDefaultPhone时创建的,传入Phone对象中。
我们看看DefaultPhoneNotifier中的notifyDataConnection函数:

@Override
public void notifyDataConnection(Phone sender, String reason, String apnType,
        PhoneConstants.DataState state) {
    doNotifyDataConnection(sender, reason, apnType, state);
}

private void doNotifyDataConnection(Phone sender, String reason, String apnType,
        PhoneConstants.DataState state) {
    //获取需要通知的参数
    ........
    try {
        //mRegistry为TelephonyRegistry的Binder代理端
        if (mRegistry != null) {
            mRegistry.notifyDataConnectionForSubscriber(....);
        }
    }catch (RemoteException ex) {
        // system process is dead
    }
}

通过上面的代码,我们知道最终DefaultPhoneNotifier将通过Binder通信,调用TelephonyRegistry的接口。

我们看看TelephonyRegistry中的notifyDataConnectionForSubscriber:

public void notifyDataConnectionForSubscriber(.....) {
    ........
    //mRecords记录了注册在TelephonyRegistry中的观察者
    synchronized (mRecords) {
        .......
        //如果状态发生改变,例如从未连接变为连接
        if (modified) {
            //轮询所有的观察者
            for (Record r : mRecords) {
                //如果观察者关注data状态的变化,并且监听的phone对应于建立连接的phone
                if (r.matchPhoneStateListenerEvent(
                        PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) &&
                        idMatch(r.subId, subId, phoneId)) {
                    try {
                        //通过回调函数进行通知
                        r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
                           mDataConnectionNetworkType[phoneId]);
                    } catch (RemoteException ex) {
                        //如果观察者已经死亡,加入移除链表
                        mRemoveList.add(r.binder);
                    }
                }
            }
            //移除异常观察者对应的注册信息
            handleRemoveListLocked();
        }
        //轮询所有的观察者,对于监听PRECISE_DATA_CONNECTION_STATE的观察者进行通知
        //与上面的相比,监听这个消息可以获得更多的dataConnection信息,但通知更为频繁(没有状态发生改变才通知的限制),同时要求更高的权限
        ...........
    }
    //发送广播进行通知
    broadcastDataConnectionStateChanged(.......);
    broadcastPreciseDataConnectionStateChanged(.........);
}

至此,我们已经分析框架是如何通知其它应用数据连接的状态了。

对于APK的开发者而言,既可以监听广播来获取数据连接的状态,也可以通过调用TelephonyManager的接口:
public void listen(PhoneStateListener listener, int events)
只需要自己创建PhoneStateListener,指定subId(决定监听哪个phone的数据连接),同时定义回调函数,并指定关注的事件(events指定,具体的值定义于PhoneStateListener.java中)。

8 ConnectivityService管理网络
根据前面的代码,我们知道DataConnection在DcActiveState中,创建出了DcNetworkAgent。DcNetworkAgent是DataConnection的内部类,继承NetworkAgent。
我们看看NetworkAgent的构造函数:

public NetworkAgent(.....) {
    .........
    ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
            Context.CONNECTIVITY_SERVICE);
    //通过ConnectivityManager将自己注册到ConnectivityService
    //注意到此处的Messenger中包裹了NetworkAgent自身,NetworkAgent继承自Handler
    //ConnectivityService将通过AsyncChannel与NetworkAgent通信
    netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
            new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);
}

ConnectivityManager通过Binder通信调用ConnectivityService中的接口:

public int registerNetworkAgent(....) {
    //权限检查
    enforceConnectivityInternalPermission();
    //利用输入参数构建NetworkAgentInfo,用来存储整个网络有关的信息
    final NetworkAgentInfo nai = new NetworkAgentInfo(......);
    .......
    //发送消息给自己的Handler处理
    mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
    return nai.network.netId;
}

接下来,ConnectivityService的handle收到消息后,调用handleRegisterNetworkAgent进行处理:

private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
    //与注册NetworkFactory一样,注册的NetworkAgent信息也会被存储到ConnectivityService
    mNetworkAgentInfos.put(na.messenger, na);
    synchronized (mNetworkForNetId) {
        mNetworkForNetId.put(na.network.netId, na);
    }
    //同样,mTrackerHandler与NetworkAgent的handler连接在一起了
    na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
    //新创建的NetworkAgentInfo,为了复用更新NetworkAgentInfo的接口,才进行了下述操作
    NetworkInfo networkInfo = na.networkInfo;
    na.networkInfo = null;
    //更新NetworkAgentInfo中的NetworkInfo
    updateNetworkInfo(na, networkInfo);
}

继续跟进updateNetworkInfo:

private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
    ..........
    //新创建的NetworkAgentInfo的created字段为false
    //DataConnection在DcActiveState将NetworkInfo的状态置为了CONNECTED
    if (!networkAgent.created
            && (state == NetworkInfo.State.CONNECTED
            || (state == NetworkInfo.State.CONNECTING && networkAgent.isVPN()))) {
        try {
            //如果建立的是VPN网络
            if (networkAgent.isVPN()) {
                //mNetd是NetworkManagementService的binder代理端
                mNetd.createVirtualNetwork(.....);
            } else {
                //对于实际的网络将进入这个分支,NetworkManagementService的操作,我们在后文再讲述
                mNetd.createPhysicalNetwork(.....);
            }
        }catch (Exception e) {
            ...........
        }
    }
    //新创建的NetworkAgentInfo进入该分支
    if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
        //更新NetworkAgentInfo中的链路信息,例如mtu,dns, 路由等
        updateLinkProperties(networkAgent, null);

        //发送消息给NetworkMonitor;NetworkMonitor也是个状态机,收到消息后,负责通进行HTTP访问,并根据返回结果,判断网络是否可用,是否需要认证
        networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
        ..............
        //判断新注册的NetworkAgent能否保留
        rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
        ..............

    //断开连接时,注销NetworkAgentInfo将进入这个分支
    } else if (state == NetworkInfo.State.DISCONNECTED) {
        ..............

    //NetworkAgent处于挂起态时,进入该分支
    } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) 
            || state == NetworkInfo.State.SUSPENDED){
        .............
    }
}

总结一下上面的代码,目前我们主要需要关注的是:通过NetworkManagementService创建实际的物理网络,更新网络的链路信息,判断NetworkAgent能否被保留。

ConnectivityService判断NetworkAgent能否被保留的原因,之前的blog中其实提过:当两个网络同时满足一个需求时,仅保留分数较高的。
因此当一个新的NetworkAgent注册到ConnectivityService时,需要判断这个NetworkAgent是否与已经注册过的NetworkAgent产生冲突。

我们看看rematchNetworkAndRequests函数:

private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
        ReapUnvalidatedNetworks reapUnvalidatedNetworks) {
    ...........
    //keep最后决定新加入的NetworkAgent是否保留
    boolean keep = newNetwork.isVPN();
    //匹配ConnectivityService初始化时创建默认NetworkRequest的NetworkAgent,将成为终端的默认网络
    boolean isNewDefault = false;
    //存储受到影响的NetworkAgentInfo
    //新加入的NetworkAgentInfo可能同时是多个networkRequest的最优匹配对象
    //于是这些NetworkRequest原来的匹配对象就是受到影响的NetworkAgentInfo
    ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
    //记录需要进行通知的对象
    //APK可以通过ConnectivityManager的接口,注册监听网络变化
    ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<NetworkRequestInfo>();
    //每一个NetworkRequest都需要进行重新匹配
    for (NetworkRequestInfo nri : mNetworkRequests.values()) {
        //取出NetworkRequest当前的最优NetworkAgent
        final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
        //判断新注册的NetworkAgent是否匹配这个NetworkRequest,即NetworkCapabilities是否能够满足
        final boolean satisfies = newNetwork.satisfies(nri.request);
        //同样的NetworkAgent,匹配情况不变
        if (newNetwork == currentNetwork && satisfies) {
            keep = true;
            continue;
        }

        //新增加的NetworkAgent匹配NetworkRequest
        if (satisfies) {
            //如果这个NetworkRequest仅用于监听
            if (!nri.isRequest()) {
                //存入相应的对象中,通知时将使用
                if (newNetwork.addRequest(nri.request)) addedRequests.add(nri);
                continue;
            }

            //如果这个匹配的NetworkRequest没有对应的NetworkAgent
            //或者对应NetworkAgent的分数小于新增NetworkAgent
            if (currentNetwork == null ||
                    currentNetwork.getCurrentScore() < newNetwork.getCurrentScore()) {
                //旧有的NetworkAgent被取代
                if (currentNetwork != null) {
                    currentNetwork.networkRequests.remove(nri.request.requestId);
                    currentNetwork.networkLingered.add(nri.request);
                    //被取代后,加入到affectedNetworks中
                    affectedNetworks.add(currentNetwork);
                } else {
                    //log
                    .......
                }
                //新增加的NetworkAgent不会被移除
                unlinger(newNetwork);
                mNetworkForRequestId.put(nri.request.requestId, newNetwork);
                if (!newNetwork.addRequest(nri.request)) {
                    ..........
                }
                addedRequests.add(nri);
                keep = true;
                //由于NetworkRequest匹配到了新的NetworkAgent,因此更新一下分数,以免NetworkFactory进行不必要的建立连接的操作
                sendUpdatedScoreToFactories(nri.request, newNetwork.getCurrentScore());

                //如果匹配的是ConnectivityService中默认的Request,那么新的NetworkAgent将成为默认使用的网络
                if (mDefaultRequest.requestId == nri.request.requestId) {
                    isNewDefault = true;
                    oldDefaultNetwork = currentNetwork;
                }
            }//下面这个分支的意思是:NetworkAgent中包含NetworkRequest但不匹配;说明NetworkAgent之前匹配,属性发生变化导致不匹配了
        } else if (newNetwork.networkRequests.get(nri.request.requestId) != null){
            newNetwork.networkRequests.remove(nri.request.requestId);
            //如果这个不再匹配的networkAgent曾经是最匹配的,那么需要更新分数,让合适的NetworkFactory建立连接
            if (currentNetwork == newNetwork) {
                mNetworkForRequestId.remove(nri.request.requestId);
                sendUpdatedScoreToFactories(nri.request, 0);
            } else {
                if (nri.isRequest()) {
                    //仅打印log,不应该进入到这个分支
                    ................
                }
            }
            //通过回调接口通知观察者,网络断开
            callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST);
        }
    }

    //处理受影响的NetworkAgent
    for (NetworkAgentInfo nai : affectedNetworks) {
        //NetworkAgent处于等待移除的状态,不用管
        if (nai.lingering) {
        } else if (unneeded(nai)) { //unneeded函数判断该NetworkAgent是否为其它NetworkRequest的最优匹配对象,如果不是就可以移除
            //NetworkMonitor发送消息进入linger状态,30s后移除无用NetworkAgent
            linger(nai);
        } else {
            //保留NetworkAgent
            unlinger(nai);
        }
    }
    //如果是新的默认网络
    if (isNewDefault) {
        //通过NetworkManagementService将该Network设置为默认网络
        makeDefault(newNetwork);
        ...............
    }
    ...............
    //如果输入参数为ReapUnvalidatedNetworks.REAP,则不经过linger状态,直接关闭无效NetworkAgent
    if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
            if (unneeded(nai)) {
                if (DBG) log("Reaping " + nai.name());
                teardownUnneededNetwork(nai);
            }
        }
    }
}

以上就是数据长连接拨号中,ConnectivityService参与的主要流程。其中,就是rematchNetworkAndRequests函数过长,导致看起来比较繁琐。
但整个过程相对而言还是比较简单的,其实就是让ConnectivityService来管理数据拨号产生的NetworkAgent,包括判断该NetworkAgent能否保留,是否需要更新其它现有的NetworkAgent。

9 NetworkManagementService创建和配置网络
我们知道Android是运行在Linux之上的,前面的拨号实际上仅在框架层形成了网络的抽象对象,还需要在Native层中形成网络抽象,这就需要依赖于NetworkManagementService了。
在前面的代码中,我们已经提到过ConnectivityService会通过NetworkManagementService创建网络,配置路由等网络属性。现在我们看看NetworkManagementService到底是如何做到的。

9.1 创建网络

//ConnectivityService中调用以下代码创建网络
//mNetd是NetworkManagementService对应的binder代理端
mNetd.createPhysicalNetwork(networkAgent.network.netId,
                            networkAgent.networkCapabilities.hasCapability(
                                    NET_CAPABILITY_NOT_RESTRICTED) ?
                                    null : NetworkManagementService.PERMISSION_SYSTEM);

看看NetworkManagementService中对应的createPhysicalNetwork:

public void createPhysicalNetwork(int netId, String permission) {
    //权限检查
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);

    try {
        if (permission != null) {
            mConnector.execute("network", "create", netId, permission);
        } else {
            //默认的数据业务,是没有permission要求的
            mConnector.execute("network", "create", netId);
        }
    } catch (NativeDaemonConnectorException e) {
        throw e.rethrowAsParcelableException();
    }
}

代码中,mConnector是NativeDaemonConnector,是用于连接NetworkManagementService与netd的。netd是Android中管理网络的守护进程。在之前的版本中,netd的启动定义与init.rc中,由init进程启动;在android 7.0中,定义于/system/netd/server/netd.rc中:

service netd /system/bin/netd
    class main
    socket netd stream 0660 root system
    socket dnsproxyd stream 0660 root inet
    socket mdns stream 0660 root system
    socket fwmarkd stream 0660 root inet

目前自己没发现netd.rc是如何集成到整个Android启动过程中的,如果有朋友知道的话,请指点一下。

接下来,我们分析一下NetworkManagementService中的代码:

//创建NetworkManagementService
public static NetworkManagementService create(Context context) throws InterruptedException {
    //NETD_SOCKET_NAME为"netd",是netd进程启动时创建的socket
    return create(context, NETD_SOCKET_NAME);
}

static NetworkManagementService create(Context context, String socket) throws InterruptedException {
    //创建NetworkManagementService,其中创建NativeDataConnector
    final NetworkManagementService service = new NetworkManagementService(context, socket);
    //service.mConnectedSignal的值为CountDownLatch(1),只用递减1次
    final CountDownLatch connectedSignal = service.mConnectedSignal;
    //NativeDataConnector连接netd
    service.mThread.start();
    //等待连接成功
    connectedSignal.await();
    return service;
}

private NetworkManagementService(Context context, String socket) {
    .............
    //创建NativeDaemonConnector,继承runnable
    //NetdCallbackReceiver为回调函数
    mConnector = new NativeDaemonConnector(new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl, FgThread.get().getLooper());
    mThread = new Thread(mConnector, NETD_TAG);
    .............
}

从上面的代码可以看出,NetworkManagementService调用service.mThread.start()后,将调用NativeDaemonConnector的run方法:

public void run() {
    mCallbackHandler = new Handler(mLooper, this);

    while (true) {
        try {
            listenToSocket();
        } catch (Exception e) {
            loge("Error in NativeDaemonConnector: " + e);
            SystemClock.sleep(5000);
        }
    }
}

private void listenToSocket() throws IOException {
    LocalSocket socket = null;

    try {
        socket = new LocalSocket();
        //返回"netd"的地址
        LocalSocketAddress address = determineSocketAddress();

        //本地socket连接netd socket
        socket.connect(address);

        InputStream inputStream = socket.getInputStream();
        synchronized (mDaemonLock) {
            mOutputStream = socket.getOutputStream();
        }

        //连接成功后,调用NetworkManagementService中定义的回调函数
        mCallbacks.onDaemonConnected();
        //后面的部分暂时不用管,其实就是接受netd socket发过来的数据
        ............
}

看看NetworkManagementService中定义的回调接口:

private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
     public void onDaemonConnected() {
        if (mConnectedSignal != null) {
            //将countDownLatch减1,触发NetworkManagementService的构造函数返回
            mConnectedSignal.countDown();
            mConnectedSignal = null;
        } else {
            mFgHandler.post(new Runnable() {
                @Override
                public void run() {
                    prepareNativeDaemon();
                }
            });
        }
    }
    ........
}

根据上面的代码,我们知道了NativeDaemonConnector创建的过程,并且知道了NativeDaemonConnector通过socket与netd进程中名为”netd”的socket相连。于是,我们就可以得出结论:NativeDaemonConnector是NetworkManagementService与netd进程通信的桥梁。

现在我们回到NetworkManagementService创建physical network的流程:

.............
//调用NativeDaemonConnector的execute方法
mConnector.execute("network", "create", netId);
.............

进入NativeDaemonConnector:

public NativeDaemonEvent execute(String cmd, Object... args)
        throws NativeDaemonConnectorException {
    //DEFAULT_TIMEOUT的时间为1min;如果一个cmd执行时间超过1min,watchdog将会杀死进程
    return execute(DEFAULT_TIMEOUT, cmd, args);
}

public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args) 
        throws NativeDaemonConnectorException {
    //调用executeForList执行
    final NativeDaemonEvent[] events = executeForList(timeoutMs, cmd, args);
    .............
}

public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
         throws NativeDaemonConnectorException {
    ................
    //记录命令发起时间
    final long startTime = SystemClock.elapsedRealtime();
    ..............
    //每个cmd的有唯一编号
    final int sequenceNumber = mSequenceNumber.incrementAndGet();
    //利用参数构造netd规定的cmd格式
    makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
    final String rawCmd = rawBuilder.toString();

    synchronized (mDaemonLock) {
        if (mOutputStream == null) {
            ............
        } else {
            try {
                //将消息发送给netd
                mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
            } catch() {
                ...........
            }
        }
    }
    NativeDaemonEvent event = null;
    do {
        //从mResponseQueue取出返回结果;mResponseQueue的类型为BlockingQueue,此处最多等待timeoutMs
        //前文中,NativeDaemonConnector的run方法中,创建socket并连接netd后,接收的消息进行解析后会放入mResponseQueue中
        event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd);
    }while (event.isClassContinue());
    //记录收到返回结果的时间
    final long endTime = SystemClock.elapsedRealtime();
    //根据返回结果判断命令是否执行异常
    ...............
}

netd守护进程以后单独分析一下,这里我们只需要知道netd中定义了CommandListener,用于处理不同的命令。
CommandListener定义于文件system/netd/server/CommandListener.cpp中:

CommandListener::CommandListener() :
        FrameworkListener("netd", true) {
    registerLockingCmd(new InterfaceCmd());
    registerLockingCmd(new IpFwdCmd());
    registerLockingCmd(new TetherCmd());
    registerLockingCmd(new NatCmd());
    registerLockingCmd(new ListTtysCmd());
    registerLockingCmd(new PppdCmd());
    registerLockingCmd(new SoftapCmd());
    registerLockingCmd(new BandwidthControlCmd(), gCtls->bandwidthCtrl.lock);
    registerLockingCmd(new IdletimerControlCmd());
    registerLockingCmd(new ResolverCmd());
    registerLockingCmd(new FirewallCmd(), gCtls->firewallCtrl.lock);
    registerLockingCmd(new ClatdCmd());
    registerLockingCmd(new NetworkCommand());
    registerLockingCmd(new StrictCmd());
    ...................
}

每种Cmd的名称基本上能概括它们的功能。
这里我们看一下NetowrkCommand:

//前面我们提过,NetworkManagementService创建网络时,第一个参数就是“network”
//因此在NativeDaemonConnector创建cmd时,指定的参数也是“network”
//netd进程收到消息后,就用对应的NetworkCommand表示
CommandListener::NetworkCommand::NetworkCommand() : NetdCommand("network") {
}

//对应的处理函数
int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc, char** argv) {
    ..........
    //根据传入参数,做相应的处理
    if (!strcmp(argv[1], "route")) {
        .............
    }
    if (!strcmp(argv[1], "interface")) {
        .............
    }
    if (!strcmp(argv[1], "create")) {
        //判断参数有效性
        .........
        //解析出framework分配的netId
        unsigned netId = stringToNetId(argv[2]);
        if (argc == 6 && !strcmp(argv[3], "vpn")) {
            //创建VPN
            ............
        } else if (argc > 4) {
            return syntaxError(client, "Unknown trailing argument(s)");
        } else {
            //默认的数据网络,是没有permission限制的
            Permission permission = PERMISSION_NONE;
            if (argc == 4) {
                permission = stringToPermission(argv[3]);
                if (permission == PERMISSION_NONE) {
                    return syntaxError(client, "Unknown permission");
                }
            }
            //调用NetworkController.cpp的createPhysicalNetwork
            if (int ret = gCtls->netCtrl.createPhysicalNetwork(netId, permission)) {
                return operationError(client, "createPhysicalNetwork() failed", ret);
            }
        }
    }
    ..............
}

NetworkController.cpp位于system/netd/server目录下:

int NetworkController::createPhysicalNetwork(unsigned netId, Permission permission) {
    //检查netId的有效性
    ..............
    //创建网络对象
    PhysicalNetwork* physicalNetwork = new PhysicalNetwork(netId, mDelegateImpl);
    //如果有权限,还要设置权限
    if (int ret = physicalNetwork->setPermission(permission)) {
        ALOGE("inconceivable! setPermission cannot fail on an empty network");
        delete physicalNetwork;
        return ret;
    }

    android::RWLock::AutoWLock lock(mRWLock);
    //保存新建的网络
    mNetworks[netId] = physicalNetwork;
    return 0;
}

至此,Android的框架完成了在Native层创建网络对象的工作。

9.2 配置网络
ConnectivityService在创建完网络后,调用了updateLinkProperties函数:

private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
    ...............
    //这些函数均通过NetworkManagementService,利用NativeDaemonConnector发送命令给Netd进程
    updateInterfaces(newLp, oldLp, netId);
    updateMtu(newLp, oldLp);
    ..............
    updateRoutes(newLp, oldLp, netId);
    updateDnses(newLp, oldLp, netId);
    ...............
}

当新建的网络成为default网络后,ConnectivityService会调用makeDefault函数:

private void makeDefault(NetworkAgentInfo newNetwork) {
    try {
        //同样利用NetworkManagementService发送命令给netd
        mNetd.setDefaultNetId(newNetwork.network.netId);
    } catch (Exception e) {
        loge("Exception setting default network :" + e);
    }
    ...................
    //TCP buffer大小是通过修改系统属性得到的
    updateTcpBufferSizes(newNetwork);
}

以上函数调用,最终均会在CommandListener中按照各种类型的Command定义的方式进行处理,用于配置网络的各种属性。其中的调用方式,与创建网络基本类似,不再做深入分析。

经过上面的分析,我们来总结一下,对于Android而言,什么叫做一个可用的网络?
其实可以认为网络就是一个可用的网卡接口(Interface),加上针对该接口的属性,例如IP地址、dns、mtu以及路由等。

前面的流程中我们知道框架拨号成功后,利用拨号返回结果中携带的信息,创建并配置了网络。这些信息利用IP地址等,是modem与网络侧协商得到的,但接口使如何分配的呢?此外,我们知道Android是运行在Linux上的,那么拨号成功后,Linux又是如何开启一个实际接口的呢?
其实这部分内容被封装在了RIL以下,由不同的厂商来实现。例如,Qualcomm中定义了NETGMRD进程,当拨号成功后,NETMGRD利用拨号得到的信息,配置Linux的数据协议栈。这部分内容是厂家的机密,就不方便写在Blog中了。

结束语
数据业务长连接拨号对应的流程比较繁琐,即使不包含RIL层以下,也很难看一遍就完全掌握。我们略去了很多细节,仅梳理了大致的脉络。
最后我们还是整理一下,整个流程涉及的类图和流程图:

类图链接


流程图链接

版权声明:转载请注明:http://blog.csdn.net/gaugamela/article

Tcp在Android7.0中无法使用的问题

最近在调试H264直播的过程中,测试同事反映在Android 7.0 的设备上无法直播,所有的TCP指令在服务端都收不到。 经过排查,是在写TCP发送数据的时候没有注意,将发送方法写在了主线...

【Android 数据业务解析】PreferredApn修改的源码分析

DcTracker中需要去获取preferredapn的id以及修改preferredapn的id,涉及到两个方法的使用,如下: getPreferredApn方法 // 得到prefer...

android 系统数据业务---打开

2数据业务打开 DcTracker的onTrySetupData调用流程图如下, onTrySetupData方法如下, protected boolean onTrySetupDa...

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

在之前的博客中,我们分析了数据业务长连接的拨号过程,现在我们来看看Android如何实现去拨号过程。...

Android7.0 数据业务基础类的创建

数据业务基础类的创建

数据业务建立流程之发起网络连接过程

经过前面这些过程,网络连接所需要的条件就全部准备就绪,接下来就是等待网络接入。         我们把网络接入过程简单分为三个阶段:         触发阶段             ----该...

数据业务建立流程之发起网络连接过程(原)

经过前面这些过程,网络连接所需要的条件就全部准备就绪,接下来就是等待网络接入。         我们把网络接入过程简单分为三个阶段:         触发阶段             ----该阶段是...

数据业务建立流程之DcTracker创建过程

一、DcTracker介绍         DcTracker是Telephony框架中负责数据业务的核心(类似于GsmCallTracker是通话业务核心,GsmServiceStateT...

数据业务建立流程之DcTracker创建过程

一、DcTracker介绍         DcTracker是Telephony框架中负责数据业务的核心(类似于GsmCallTracker是通话业务核心,GsmServiceStateT...
  • jrunw
  • jrunw
  • 2017年03月06日 16:17
  • 60
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android7.0 数据业务长连接拨号过程
举报原因:
原因补充:

(最多只允许输入30个字)