Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android7.0 數據業務長連接撥號過程

Android7.0 數據業務長連接撥號過程

編輯:關於Android編程

前面我們已經分析了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 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, 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 affectedNetworks = new ArrayList();
    //記錄需要進行通知的對象
    //APK可以通過ConnectivityManager的接口,注冊監聽網絡變化
    ArrayList addedRequests = new ArrayList();
    //每一個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層以下,也很難看一遍就完全掌握。我們略去了很多細節,僅梳理了大致的脈絡。
最後我們還是整理一下,整個流程涉及的類圖和流程圖:

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved