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

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

編輯:關於Android編程

在之前的博客中,我們分析了數據業務長連接的撥號過程,現在我們來看看Android如何實現去撥號過程。

與撥號過程一樣,用戶也是通過點擊設置界面,發起主動斷開數據連接的命令。
從界面到框架,同樣經過了CellDataPreference、TelephonyManager、PhoneInterfaceManager、GsmCdmaPhone和DcTracker的處理。去撥號和撥號調用的框架接口是一樣的,只是參數不一致。
最終,從DcTracker的onSetUserDataEnabled函數開始,去撥號與撥號的流程產生了分歧:

1 DcTracker

//去撥號時參數為false
private void onSetUserDataEnabled(boolean enabled) {
    synchronized (mDataEnabledLock) {
        if (mUserDataEnabled != enabled) {
            mUserDataEnabled = enabled;
            //寫數據庫,某些情況下發送通知
            ..............
            if (enabled) {
                //進入撥號流程
                onTrySetupData(Phone.REASON_DATA_ENABLED);
            } else {
                //進入去撥號號流程
                onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
            }
        }
    }
}

private void onCleanUpAllConnections(String cause) {
    cleanUpAllConnections(true, cause);
}

我們進一步看看cleanUpAllConnections:

private boolean cleanUpAllConnections(boolean tearDown, String reason) {
    boolean didDisconnect = false;
    boolean specificDisable = false;

    if (!TextUtils.isEmpty(reason)) {
        //主動撥號的reason為REASON_DATA_SPECIFIC_DISABLED,將specificDisable置為true
        specificDisable = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED) ||
                reason.equals(Phone.REASON_ROAMING_ON);
    }

    for (ApnContext apnContext : mApnContexts.values()) {
        //根據ApnContext中記錄的狀態,判斷是否有未斷開的連接
        if (apnContext.isDisconnected() == false) didDisconnect = true;

        //specificDisable的值為true
        if (specificDisable) {
            ApnSetting apnSetting = apnContext.getApnSetting();

            //默認的數據撥號,均滿足以下條件
            //isMetered的返回值,由CarrierConfigManager中的配置決定,default類型將返回true
            if (apnSetting != null && apnSetting.isMetered(mPhone.getContext(),
                    mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())) {
                apnContext.setReason(reason);

                //進入斷開連接的流程
                cleanUpConnection(tearDown, apnContext);
            }
        } else {
            ............
        }
    }

    //不再檢測數據業務數據的收發的情況
    //數據業務上下行圖標消失
    stopNetStatPoll();
    //同樣,不再檢測數據業務是否僅發送無接收
    stopDataStallAlarm();
    ..................
}

隨著流程我們進入到cleanUpConnection:

private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
    ..............
    //取出ApnContext中存儲的DcTracker與DataConnection通信的AsyncChannel
    DcAsyncChannel dcac = apnContext.getDcAc();
    ..............
    if (tearDown) {
        if (apnContext.isDisconnected()) {
            ............
        } else {
            if (dcac != null) {
                if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
                    ..................
                    //取出將要斷開的DataConnection的id,打印log和記錄用
                    final int generation = apnContext.getConnectionGeneration();
                    ..................
                    Pair pair =
                            new Pair(apnContext, generation);

                    //斷開處理完後,將消息返回給DcTracker處理
                    Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, pair);

                    //APN類型為DUN才會進入
                    if (disconnectAll) {
                        ...........
                    } else {
                        //利用DcAsyncChannel向DataConnection發送消息
                        apnContext.getDcAc()
                            .tearDown(apnContext, apnContext.getReason(), msg);
                    }
                    //更新ApnContext的狀態
                    apnContext.setState(DctConstants.State.DISCONNECTING);
                    mDisconnectPendingCount++;
                }
            }
        }
    } else {
        ............
    }

    if (dcac != null) {
        cancelReconnectAlarm(apnContext);
    }
    ............
}

我們看看DcAsyncChannel的tearDown函數:

public void tearDown(ApnContext apnContext, String reason, Message onCompletedMsg) {
    ............
    sendMessage(DataConnection.EVENT_DISCONNECT,
            new DisconnectParams(apnContext, reason, onCompletedMsg));
}

從上面的代碼可以看到,DcAsyncChannel將向DataConnection發送EVENT_DISCONNECT消息。

2 DataConnection

2.1 DcActiveState
在長連接撥號的博客中,我們已經提過,當數據業務撥號成功後,DataConnection會處於DcActiveState,因此去撥號時將由DcActiveState來處理EVENT_DISCONNECT消息:

private class DcActiveState extends State {
    ..............
    @Override
    public boolean processMessage(Message msg) {
        boolean retVal;

        switch (msg.what) {
            ...........
            case EVENT_DISCONNECT: {
                DisconnectParams dp = (DisconnectParams) msg.obj;
                ............
                if (mApnContexts.containsKey(dp.mApnContext)) {
                    .............
                    if (mApnContexts.size() == 1) {
                        mApnContexts.clear();
                        ...................
                        //利用RIL向modem發送DEACTIVATE_DATA_CALL消息
                        //發送成功後,RIL將返回DataConnection EVENT_DEACTIVATE_DONE
                        tearDownData(dp);

                        //DataConnection轉移狀態
                        transitionTo(mDisconnectingState);
                    } else {
                        //注意到當一個DataConnection被多個ApnContext復用時
                        //去撥號只會移除這個DataConnection,並沒有進行斷開操作
                        mApnContexts.remove(dp.mApnContext);
                        notifyDisconnectCompleted(dp, false);
                    }
                } else {
                    .............
                }
                retVal = HANDLED;
                break;
            }
        }
        ...........
    }
}

在DataConnection離開DcActiveState時,將調用其中的exit函數:

@Override
public void exit() {
    ............
    //不用再監聽通話的開始與結束
    mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
    mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());

    mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
            reason, mNetworkInfo.getExtraInfo());
    if (mNetworkAgent != null) {
        //更新NetworkAgent的狀態,將通知ConnectivityService移除網絡,後文分析
        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
        mNetworkAgent = null;
    }
}

2.2 DcDisconnectingState

private class DcDisconnectingState extends State {
    @Override
    public boolean processMessage(Message msg) {
        boolean retVal;

        switch (msg.what) {
            ............
            case EVENT_DEACTIVATE_DONE:
                AsyncResult ar = (AsyncResult) msg.obj;
                DisconnectParams dp = (DisconnectParams) ar.userObj;
                ..................
                //保證發送和接受的消息是一一對應的
                if (dp.mTag == mTag) {
                    ................
                    //回到最初未撥號時的InactiveState
                    transitionTo(mInactiveState);
                } else {
                    ...............
                }
                retVal = HANDLED;
                break;
            .............
        }
        return return retVal;
    }
}

2.3 DcInactiveState
從其它狀態進入DcInactiveState時,需要調用對應的enter函數:

private class DcInactiveState extends State {
    ...........
    @Override
    public void enter() {
        ...........
        if (mDisconnectParams != null) {
            ..............
            //發送EVENT_DISCONNECT_DONE消息給DcTracker處理
            notifyDisconnectCompleted(mDisconnectParams, true);
        }
        ...........
    }
    ...........
}

至此,數據業務長連接去撥號的流程基本結束了,我們只需要再看看ConnectivityService移除NetworkAgent的操作,以及DcTracker收到去撥號完成消息後的處理流程。

3 ConnectivityService
前面的代碼已經提到,在DataConnection離開DcActiveState時,調用NetworkAgent的sendNetworkInfo函數,其中NetworkAgent的NetworkInfo已經處於DISCONNECTED狀態。
NetworkAgent中sendNetworkInfo函數如下:

public void sendNetworkInfo(NetworkInfo networkInfo) {
    //其實就是利用NetworkAgent與ConnectivityService連接的AsyncChannel,向ConnectivityService發送消息
    queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
}

ConnectivityService中的NetworkStateTrackerHandler將處理該信息:

.............
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
    NetworkInfo info = (NetworkInfo) msg.obj;
    //之前撥號時,也調用過updateNetworkInfo
    updateNetworkInfo(nai, info);
    break;
}
.............

隨著流程我們再次分析一下updateNetworkInfo:

private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
    ............
    if (!networkAgent.created
            && (state == NetworkInfo.State.CONNECTED
            || (state == NetworkInfo.State.CONNECTING && networkAgent.isVPN()))) {
        //撥號流程創建NetworkAgent的工作
        .................
    }

    if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
        //執行撥號對應的操作
        ........
    } else if (state == NetworkInfo.State.DISCONNECTED) {
        //ConnectivityService斷開與NetworkAgent的通信
        networkAgent.asyncChannel.disconnect();
        if (networkAgent.isVPN()) {
            //VPN相關
            .................
        }
    } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
            state == NetworkInfo.State.SUSPENDED) {
        //suspend, 目前不關注
        ............
    }
}

進入到AsyncChannel中的disconnect函數:

public void disconnect() {
    if ((mConnection != null) && (mSrcContext != null)) {
        //解除源端的綁定
        mSrcContext.unbindService(mConnection);
        mConnection = null;
    }

    try {
        Message msg = Message.obtain();
        msg.what = CMD_CHANNEL_DISCONNECTED;
        msg.replyTo = mSrcMessenger;
        //發送消息給NetworkAgent
        mDstMessenger.send(msg);
    } catch(Exception e) {
    }

    //回復消息給ConnectivityService
    replyDisconnected(STATUS_SUCCESSFUL);
    mSrcHandler = null;
    .................
}

private void replyDisconnected(int status) {
    if (mSrcHandler == null) return;
    Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
    msg.arg1 = status;
    msg.obj = this;
    msg.replyTo = mDstMessenger;
    mSrcHandler.sendMessage(msg);
}

ConnectivityService中的NetworkStateTrackerHandler收到消息後,將調用handleAsyncChannelDisconnected進行處理:

private void handleAsyncChannelDisconnected(Message msg) {
    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
    if (nai != null) {
        //清除與NetworkAgent相關的信息,同時通過回調通知觀察者
        ................
        for (int i = 0; i < nai.networkRequests.size(); i++) {
            NetworkRequest request = nai.networkRequests.valueAt(i);
            NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
            //NetworkAgent如果是其它Request的最優匹配對象,那麼當該NetworkAgent被移除時
            //需要更新分數通知給NetworkFactory,讓它們嘗試重新聯網,以選出新的最優NetworkAgent
            if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
                mNetworkForRequestId.remove(request.requestId);
                sendUpdatedScoreToFactories(request, 0);
            }
            ........
            //讓所有的NetworkAgent與NetworkRequest進行重新匹配
            //移除一個NetworkAgent後,感覺不需要進行這個操作;畢竟上面已經做過sendUpdatedScoreToFactories的操作,會觸發重新匹配的
            rematchAllNetworksAndRequests(null, 0);
            if (nai.created) {
                try {
                    //移除Native層創建的網絡對象,會同時移除對應的路由規則
                    //這裡也是通過NetworkManagementService到CommandListener中完成的
                    //與撥號流程
                    mNetd.removeNetwork(nai.network.netId);
                } catch (Exception e) {
                    loge("Exception removing network: " + e);
                }
                ..........
            }
        }
    } else {
        ............
    }
}

至此,去撥號流程中,ConnectivityService的工作就完成了。

4 DcTracker
在DataConnection進入到DcInactiveState後,將向DcTracker發送EVENT_DISCONNECT_DONE消息,DcTracker將調用onDisconnectDone進行處理。

private void onDisconnectDone(AsyncResult ar) {
    //取出去撥號的apnContext
    ApnContext apnContext = getValidApnContext(ar, "onDisconnectDone");
    if (apnContext == null) return;

    //重新置為Idle態
    apnContext.setState(DctConstants.State.IDLE);

    //通過DefaultPhoneNotifier,TelephonyRegistry通知給APK
    mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());

    //飛行模式相關的判斷
    ...................

    //此時,APN仍處於激活狀態;滿足重撥條件時,會嘗試撥號
    if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
        ............
        long delay = apnContext.getInterApnDelay(mFailFast);
        if (delay > 0) {
            //經過一段時間後,將發送Intent,對應action為INTENT_RECONNECT_ALARM
            startAlarmForReconnect(delay, apnContext);
        }
    } else {
        .............
    }

    if (mDisconnectPendingCount > 0)
        mDisconnectPendingCount--;

    if (mDisconnectPendingCount == 0) {
        ..................
        //通知觀察者,數據連接斷開
        notifyDataDisconnectComplete();
        notifyAllDataDisconnected();
        ..................
    }
}

DcTrackerINTENT_RECONNECT_ALARM後,調用onActionIntentReconnectAlarm進行處理:

private void onActionIntentReconnectAlarm(Intent intent) {
    ..............
    if ((apnContext != null) && (apnContext.isEnabled())) {
        ................
        if ((apnContextState == DctConstants.State.FAILED)
                || (apnContextState == DctConstants.State.IDLE)) {
            DcAsyncChannel dcac = apnContext.getDcAc();
            if (dcac != null) {
                dcac.tearDown(apnContext, "", null);
            }
            apnContext.setDataConnectionAc(null);
            apnContext.setState(DctConstants.State.IDLE);
        } else {
            ............
        }

        //重新發起撥號請求;由於數據開關已經關閉,不會再進行實際的撥號
        sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
        apnContext.setReconnectIntent(null);
    }
}

結束語
以上就是長連接去撥號的主要過程。與撥號流程相比,去撥號流程相對簡單,涉及的類也與撥號基本相似,這裡就不再補充類圖和流程圖了。

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