Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 4.0 framework 數據業務學習總結(1)

Android 4.0 framework 數據業務學習總結(1)

編輯:關於Android編程

簡介
本條目用於記錄本人對Android framework側數據業務的階段學習總結。

內容包括流程圖,代碼分析,BUG用例等。

第一階段學習成果
本階段主要注重對數據連接設置管理流程的學習,掌握數據業務的基本流程與構造。同時嘗試解決部分簡單BUG。

數據連接設置管理介紹
通過Setting的常規設置頁面中的數據連接開關,打開/關閉數據連接業務。

數據業務設置完成後如何更新到status bar上。

開機後如何自啟動數據業務。

這些過程是基本數據業務之一。

數據連接設置管理流程圖

 

數據連接設置管理代碼分析
通過Settings控制數據連接開關
Settings.java文件提供動作的入口。通過ConnectivityManager實例的setMobileDataEnable方法設置此開關。

    /**
     * Invoked on each preference click in this hierarchy, overrides
     * PreferenceActivity's implementation.  Used to make sure we track the
     * preference click events.
     */
    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
        /** TODO: Refactor and get rid of the if's using subclasses */
        ...
        } else if (preference == mButtonDataEnabled) {
            if (DBG) log("onPreferenceTreeClick: preference == mButtonDataEnabled.");
            ConnectivityManager cm =
                    (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
            cm.setMobileDataEnabled(mButtonDataEnabled.isChecked());
            return true;
  ...
 }

這個方法實際上調用的是ConnectivityService實例的setMobileDataEnable方法。

     /**
     * @see ConnectivityManager#setMobileDataEnabled(boolean)
     */
    public void setMobileDataEnabled(boolean enabled) {
        enforceChangePermission();
        if (DBG) log("setMobileDataEnabled(" + enabled + ")");
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
                (enabled ? ENABLED : DISABLED), 0));
    }

通過Handler回調機制,我們在handleMessage對消息EVENT_SET_MOBILE_DATA進行處理。

走到了handleSetMobileData方法中。

     private void handleSetMobileData(boolean enabled) {
        if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
            if (VDBG) {
                log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
            }
            mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled);
        }
        if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) {
            if (VDBG) {
                log(mNetTrackers[ConnectivityManager.TYPE_WIMAX].toString() + enabled);
            }
            mNetTrackers[ConnectivityManager.TYPE_WIMAX].setUserDataEnable(enabled);
        }
    }

該方法根據傳入的動作(enable or disable),設置對應的networkStateTracker標志位狀態。

起作用的方法也就是MobileDataStateTracker實例的setUserDataEnable方法。

     @Override
    public void setUserDataEnable(boolean enabled) {
        if (DBG) log("setUserDataEnable: E enabled=" + enabled);
        final AsyncChannel channel = mDataConnectionTrackerAc;
        if (channel != null) {
            channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,
                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
            mUserDataEnabled = enabled;
        }
        if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
    }

同樣使用了Handler的回調機制,通過回調之後的消息

DctConstants.CMD_SET_USER_DATA_ENABLE找到對應的在DataConnectionTracker裡的Handler處理方法線程。

             case DctConstants.CMD_SET_USER_DATA_ENABLE: {
                final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
                if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
                onSetUserDataEnabled(enabled);
                break;
            }

即回調至方法onSetUserDataEnabled()中

     protected void onSetUserDataEnabled(boolean enabled) {
        synchronized (mDataEnabledLock) {
            final boolean prevEnabled = getAnyDataEnabled();
            if (mUserDataEnabled != enabled) {
                mUserDataEnabled = enabled;
                Settings.Global.putInt(mPhone.getContext().getContentResolver(),
                        Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
                if (getDataOnRoamingEnabled() == false &&
                        mPhone.getServiceState().getRoaming() == true) {
                    if (enabled) {
                        notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
                    } else {
                        notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
                    }
                }
                if (prevEnabled != getAnyDataEnabled()) {
                    if (!prevEnabled) {
                        resetAllRetryCounts();
                        onTrySetupData(Phone.REASON_DATA_ENABLED);
                    } else {
                        onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
                    }
                }
            }
        }
    }

這個方法主要可分成兩部分理解。

傳進來的enable動作影響了mUserDataEnable,同時也作為數據存放到數據庫中。

如果這次動作傳入有效(prevEnable與AnyDataEnable之間發生變化),

當enabled 為 TRUE(即開啟數據連接開關) ,那麼就有一次成功的建立鏈接動作(調用resetAllRetryCounts重置計數器,然後通過onTrySetupData建立鏈接)。

當enabled 為 FALSE (即關閉數據連接開關),那麼就清空所有的鏈接(調用onCleanUpAllConnections)。

 


如果成功的話,最終這個動作走入gsmDataConnectionTracker的setupData方法。

通過bringUp方法建立數據鏈接。

注意帶上了what=DctConstants.EVENT_DATA_SETUP_COMPLETE消息的msg方法是當該動作完成後才被回調。具體的建立鏈接動作進入bringUp中查看。

        Message msg = obtainMessage();
        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
        msg.obj = apnContext;
        dc.bringUp(msg, apn);
        if (DBG) log("setupData: initing!");
        return true;

進到DataConnection.java中,發現這個方法仍然是個回調處理機制。

     /**
     * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
     * Used for cellular networks that use Acesss Point Names (APN) such
     * as GSM networks.
     *
     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
     *        With AsyncResult.userObj set to the original msg.obj,
     *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
     * @param apn is the Access Point Name to bring up a connection to
     */
    public void bringUp(Message onCompletedMsg, ApnSetting apn) {
        sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
    }

假設這是個建立鏈接的過程,而不是斷開鏈接的過程。那麼實際上走入InactiveState這個case中。

         @Override
        public boolean processMessage(Message msg) {
            boolean retVal;
            switch (msg.what) {
   ...
                case EVENT_CONNECT:
                    ConnectionParams cp = (ConnectionParams) msg.obj;
                    cp.tag = mTag;
                    if (DBG) {
                        log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
                                + mRefCount);
                    }
                    mRefCount = 1;
                    onConnect(cp);
                    transitionTo(mActivatingState);
                    retVal = HANDLED;
                    break;
    }
}

除了上述狀態外,EVENT_CONNECT時數據鏈路的狀態還可能是ActivatingState、ActiveState以及DisconnectingState。關於這幾種狀態之間的轉換關系,可以由下圖表示(圖源:再論android 2.2數據連接過程 - Armily's Tech Blog - 博客頻道 - CSDN.NET):

 

之後進入GsmDataConnection的onCreate()方法。

     /**
     * Begin setting up a data connection, calls setupDataCall
     * and the ConnectionParams will be returned with the
     * EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj.
     *
     * @param cp is the connection parameters
     */
    @Override
    protected
    void onConnect(ConnectionParams cp) {
        mApn = cp.apn;
        if (DBG) log("Connecting to carrier: '" + mApn.carrier
                + "' APN: '" + mApn.apn
                + "' proxy: '" + mApn.proxy + "' port: '" + mApn.port);
        createTime = -1;
        lastFailTime = -1;
        lastFailCause = FailCause.NONE;
        // msg.obj will be returned in AsyncResult.userObj;
        Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
        msg.obj = cp;
        int authType = mApn.authType;
        if (authType == -1) {
            authType = TextUtils.isEmpty(mApn.user) ? RILConstants.SETUP_DATA_AUTH_NONE
                    : RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
        }
        String protocol;
        if (phone.getServiceState().getRoaming()) {
            protocol = mApn.roamingProtocol;
        } else {
            protocol = mApn.protocol;
        }
        phone.mCM.setupDataCall(
                Integer.toString(getRilRadioTechnology(RILConstants.SETUP_DATA_TECH_GSM)),
                Integer.toString(mProfileId),
                mApn.apn, mApn.user, mApn.password,
                Integer.toString(authType),
                protocol, msg);
    }

看到最後仍然是通過ril的方法setupDataCall以socket方式發送下去。

     public void
    setupDataCall(String radioTechnology, String profile, String apn,
            String user, String password, String authType, String protocol,
            Message result) {
        RILRequest rr
                = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
        rr.mp.writeInt(7);
        rr.mp.writeString(radioTechnology);
        rr.mp.writeString(profile);
        rr.mp.writeString(apn);
        rr.mp.writeString(user);
        rr.mp.writeString(password);
        rr.mp.writeString(authType);
        rr.mp.writeString(protocol);
        if (RILJ_LOGD) riljLog(rr.serialString() + "> "
                + requestToString(rr.mRequest) + " " + radioTechnology + " "
                + profile + " " + apn + " " + user + " "
                + password + " " + authType + " " + protocol);
        send(rr);
    }

這個就是建立數據鏈接,從app到ril的流程。

數據連接設置成功後的消息反饋機制
接下來關注消息EVENT_DATA_SETUP_COMPLETE的回調,EVENT_DATA_SETUP_COMPLETE消息在數據連接setupData動作完成時被捕獲。

其作用是對本次連接的結果進行處理。

[email protected]

             case DctConstants.EVENT_DATA_SETUP_COMPLETE:
                mCidActive = msg.arg1;
                onDataSetupComplete((AsyncResult) msg.obj);
                break;

即回調的處理方法是onDataSetupComplete()@GsmDataConnectionTracker.java。

該方法針對可能出現的連接失效情況進行異常處理或重連。

如果連接建立成功,同時其他設置皆完善的情況下,最後會以notify的方式通知Phone實例數據業務建立成功。

                DataConnection dc = apnContext.getDataConnection();
                ApnSetting apn = apnContext.getApnSetting();
                if (DBG) {
                    log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
                }
                if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
                    try {
                        String port = apn.port;
                        if (TextUtils.isEmpty(port)) port = "8080";
                        ProxyProperties proxy = new ProxyProperties(apn.proxy,
                                Integer.parseInt(port), null);
                        dcac.setLinkPropertiesHttpProxySync(proxy);
                    } catch (NumberFormatException e) {
                        loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
                                apn.port + "): " + e);
                    }
                }
                // everything is setup
                if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
                    SystemProperties.set("gsm.defaultpdpcontext.active", "true");
                    if (canSetPreferApn && mPreferredApn == null) {
                        if (DBG) log("onDataSetupComplete: PREFERED APN is null");
                        mPreferredApn = apn;
                        if (mPreferredApn != null) {
                            setPreferredApn(mPreferredApn.id);
                        }
                    }
                } else {
                    SystemProperties.set("gsm.defaultpdpcontext.active", "false");
                }
                notifyDefaultData(apnContext);

這個方法最終調用DefaultPhoneNotifier.java實例的doNotifyDataConnection()方法。

如果上層在NetworkController.java裡已經注冊過對應的消息。

     /**
     * Construct this controller object and register for updates.
     */
    public NetworkController(Context context) {
       ...
          // telephony
        mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
        mPhone.listen(mPhoneStateListener,
                          PhoneStateListener.LISTEN_SERVICE_STATE
                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
                        | PhoneStateListener.LISTEN_CALL_STATE
                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
  }

那麼該方法用於相應這些注冊消息。

        try {
            mRegistry.notifyDataConnection(
                    convertDataState(state),
                    sender.isDataConnectivityPossible(tmpApnType[0]), reason,
                    sender.getActiveApnHost(tmpApnType[0]),
                    apnType,
                    linkProperties,
                    linkCapabilities,
                    ((telephony!=null) ? telephony.getNetworkType() :
                    TelephonyManager.NETWORK_TYPE_UNKNOWN),
                    roaming);
        } catch (RemoteException ex) {
            // system process is dead
        }

找到對應實現方法處。

在TelephonyRegistry.java實例的notfiyDataConnection()方法中,有

            if (modified) {
                if (DBG) {
                    Slog.d(TAG, "onDataConnectionStateChanged(" + mDataConnectionState
                        + ", " + mDataConnectionNetworkType + ")");
                }
                for (Record r : mRecords) {
                    if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
                        try {
                            r.callback.onDataConnectionStateChanged(mDataConnectionState,
                                    mDataConnectionNetworkType);
                        } catch (RemoteException ex) {
                            mRemoveList.add(r.binder);
                        }
                    }
                }
                handleRemoveListLocked();
            }
        }
        broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
                apnType, linkProperties, linkCapabilities, roaming);

注意mRecords.events實際上就是之前在NetworkController.java中注冊的消息。如果這些消息存在,同時LISTEN_DATA_CONNECTION_STATE狀態正常,那麼就回調各自對應的onDataConnectionStateChanged()方法。

這是典型的OBSERVER模式。

最終這個回調的實現在消息所注冊的類NetworkController.java中。

               @Override
        public void onDataConnectionStateChanged(int state, int networkType) {
            if (DEBUG) {
                Slog.d(TAG, "onDataConnectionStateChanged: state=" + state
                        + " type=" + networkType);
            }
            mDataState = state;
            mDataNetType = networkType;
            updateDataNetType();
            updateDataIcon();
            refreshViews();
        }
 

該方法實現了status bar上對於數據業務圖標的顯示和更新。

開機後系統自動啟動數據連接業務
如果關機前用戶已經開啟了“數據連接”業務,且未進入飛行模式。

那麼重新開機後,系統應該自動進入“數據連接”的連接態。

具體流程如下:

注意到GsmDataConnectionTracker.java中會監聽已注冊的關於數據連接相關的消息。在handler中進行處理:

         switch (msg.what) {
            case EVENT_RECORDS_LOADED:
                onRecordsLoaded();
                break;
            case EVENT_DATA_CONNECTION_DETACHED:
                onDataConnectionDetached();
                break;
            case EVENT_DATA_CONNECTION_ATTACHED:
                onDataConnectionAttached();
                break;
            case EVENT_DATA_STATE_CHANGED:
                onDataStateChanged((AsyncResult) msg.obj);
                break;
            case EVENT_POLL_PDP:
                onPollPdp();
                break;
            case EVENT_DO_RECOVERY:
                doRecovery();
                break;
            case EVENT_APN_CHANGED:
                onApnChanged();
                break;
            case EVENT_PS_RESTRICT_ENABLED:
                /**
                 * We don't need to explicitly to tear down the PDP context
                 * when PS restricted is enabled. The base band will deactive
                 * PDP context and notify us with PDP_CONTEXT_CHANGED.
                 * But we should stop the network polling and prevent reset PDP.
                 */
                if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
                stopNetStatPoll();
                stopDataStallAlarm();
                mIsPsRestricted = true;
                break;
            case EVENT_PS_RESTRICT_DISABLED:
                /**
                 * When PS restrict is removed, we need setup PDP connection if
                 * PDP connection is down.
                 */
                if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
                mIsPsRestricted  = false;
                if (isConnected()) {
                    startNetStatPoll();
                    startDataStallAlarm();
                } else {
                    // TODO: Should all PDN states be checked to fail?
                    if (mState == State.FAILED) {
                        cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
                        resetAllRetryCounts();
                        mReregisterOnReconnectFailure = false;
                    }
                    trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, Phone.APN_TYPE_DEFAULT);
                }
                break;
            case EVENT_TRY_SETUP_DATA:
                onTrySetupData(msg);
                break;
            case EVENT_CLEAN_UP_CONNECTION:
                boolean tearDown = (msg.arg1 == 0) ? false : true;
                if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
                if (msg.obj instanceof ApnContext) {
                    cleanUpConnection(tearDown, (ApnContext)msg.obj);
                } else {
                    loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context");
                }
                break;
            case EVENT_RAT_CHANGED:
                onRatChanged();
                break;
            default:
                // handle the message in the super class DataConnectionTracker
                super.handleMessage(msg);
                break;
        }

這些消息被捕獲到時,會回調對應的方法。以sim卡成功被Load為例。

將會回調onRecordsLoaded()方法。

     private void onRecordsLoaded() {
        if (DBG) log("onRecordsLoaded: createAllApnList");
        createAllApnList();
        if (mPhone.mCM.getRadioState().isOn()) {
            if (DBG) log("onRecordsLoaded: notifying data availability");
            notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
        }
        setupDataOnReadyApns(Phone.REASON_SIM_LOADED);
    }

即將相應的reason送給了SetupDataOnReadyApn()方法。

與之類似的,其他消息被回調時,最終都會調用該方法來完成消息的傳遞。

SetupDataOnReadyApn()方法在確認apnContext無誤之後,會調用之前提到的trySetupData()方法。

         // Only check for default APN state
        for (ApnContext apnContext :
                    getPrioritySortedApnContextList().toArray(new ApnContext[0])) {
            if (apnContext.getState() == State.FAILED) {
                // By this time, alarms for all failed Apns
                // should be stopped if any.
                // Make sure to set the state back to IDLE
                // so that setup data can happen.
                apnContext.setState(State.IDLE);
            }
            if (apnContext.isReady()) {
                if (apnContext.getState() == State.IDLE ||
                        (apnContext.getState() == State.CONNECTED &&
                                apnContext.getDataConnectionAc().getPartialSuccessStatusSync())) {
                    apnContext.setReason(reason);
                    trySetupData(apnContext);
                }
            }
        }

最終與Settings設置的方式,將對應的消息以socket方式發給rild。

完成開機後即可以自動設置“數據連接”的動作。

 


至此,整個數據業務從設置到status bar的顯示過程完成了。如有誤請指出。

 

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