Android建立GPRS通信的流程

(1).应用程序中的入口

从settings应用程序中,选择移动网络,进入到 Phone应用程序的移动网络设置界面 (actionbar的应用程序图标可以明确看出,开始的时候以为还是在 settings程序中,直接去找,找了好半天也没有找到,最后才想起根据 settings列表的点击事件去查看执行了什么操作,才发现已经进入了 Phone程序,看来很多东西不能只凭眼睛看到的就下结论 ),在这里启用数据连接。对应源码为:
alps/packages/apps/Phone/src/com/android/phone/MobileNetworkSettings.java
现看看该类的注释:
Mobile network settings” screen. This preference screen lets you enable/disable mobile data, and control data
roaming and other network-specific mobile data features. It’s used on non-voice-capable tablets as well as regular
phone devices. Note that this PreferenceActivity is part of the phone app, even though you reach it from the “Wireless
& Networks” section of the main Settings app. It’s not part of the “Call settings” hierarchy that’s available from the
Phone app (see CallFeaturesSetting for that.)
移动网络设置界面。此首选项允许启用禁用移动数据网络,并且控制数据漫游以及其他指定的移动数据网络。它是用于 non-voice-capable平板电脑以及常规电话设备。 注意,这个PreferenceActivity 是phone应用程序 的一部分,即使你是通过 settings程序的“无线&网络”选项到达这里。这不是” Call Setting”层次结构的一部分。
//String keys for preference lookup
private static final String BUTTON_DATA_ENABLED_KEY = “button_data_enabled_key” ; //启用数据网络的key
private static final String BUTTON_DATA_USAGE_KEY = “button_data_usage_key” ;
private static final String BUTTON_PREFERED_NETWORK_MODE = “preferred_network_mode_key” ;
private static final String BUTTON_ROAMING_KEY = “button_roaming_key”;
private static final String BUTTON_CDMA_LTE_DATA_SERVICE_KEY = “cdma_lte_data_service_key” ;

通过 BUTTON_DATA_ENABLED_KEY 找到相应的 CheckBoxPreference,在onCreate() 方法中有:
@Override
protected void onCreate(Bundle icicle) {
super .onCreate(icicle);

    addPreferencesFromResource(R.xml.network_setting);

……
mDataConnPref = (DefaultSimPreference) prefSet.findPreference( KEY_DATA_CONN);
mDataConnPref .setOnPreferenceChangeListener(this);
mButtonDataEnabled = (CheckBoxPreference) prefSet.findPreference(BUTTON_DATA_ENABLED_KEY );
mButtonDataRoam = (CheckBoxPreference) prefSet.findPreference(BUTTON_ROAMING_KEY);
mButtonDataRoam .setSummaryOn(mExtension .getRoamingSummary( this,R.string.roaming_enable));
mButtonDataRoam .setSummaryOff(mExtension .getRoamingSummary( this,R.string.roaming_disable));
mButtonPreferredNetworkMode = (ListPreference) prefSet.findPreference(
BUTTON_PREFERED_NETWORK_MODE );
mButtonDataUsage = prefSet.findPreference(BUTTON_DATA_USAGE_KEY);
……
}
从oncreate方法中看到 mButtonDataEnabled 没有注册 PreferenceChangeListener事件,因此将不会触发onPreferenceChange()方法,仅触发 onPreferenceTreeClick( PreferenceScreen preferenceScreen, Preference preference)方法。在 preferenceActivity中如果监听了PreferenceChangeListener和 OnPreferenceClickListener,那么这onPreferenceTreeClick ,onPreferenceChange, onPreferenceClick三者的触发是怎么回事呢?

a.先调用onPreferenceClick() 方法,如果该方法返回 true,则不再调用onPreferenceTreeClick方法 ; 如果 onPreferenceClick方法返回false ,则继续调用 onPreferenceTreeClick方法。
b.onPreferenceChange的方法独立与其他两种方法的运行。也就是说,它总是会运行。
补充:点击某个Preference 控件后,会先回调 onPreferenceChange()方法,即是否保存值,然后再回调 onPreferenceClick以及onPreferenceTreeClick() 方法,因此在 onPreferenceClick/onPreferenceTreeClick方法中我们得到的控件值就是最新的 Preference控件值。
详细可以看: http://blog.csdn.net/qinjuning/article/details/6710003

接下来看看当前 MobileNetworkSettings的onPreferenceTreeClick 方法:
public boolean onPreferenceTreeClick( PreferenceScreen preferenceScreen, Preference preference) {

……..
else if (preference == mButtonDataEnabled) {
if (DBG ) {
log(“onPreferenceTreeClick: preference == mButtonDataEnabled.”);
}
///M: change the interface definition for consistent_UI
if (!mExtension .dataEnableReminder(mButtonDataEnabled.isChecked(), this)) {
Log.d( LOG_TAG, “onPreferenceTreeClick: preference == mButtonDataEnabled.” );
if (mButtonDataEnabled .isChecked() && isSimLocked()) {
mCellConnMgr.handleCellConn(0, PIN1_REQUEST_CODE);
Log.d( LOG_TAG, “Data enable check change request pin single card” );
mButtonDataEnabled.setChecked( false);
} else {
mIsChangeData = true ;
NetworkInfo networkInfo = mConnService.getActiveNetworkInfo();
if (!(networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI
&& networkInfo.isConnected())) {
showDialog( PROGRESS_DIALOG);
}
mConnService.setMobileDataEnabled( mButtonDataEnabled .isChecked());
mH.sendMessageDelayed( mH .obtainMessage( DATA_STATE_CHANGE_TIMEOUT), 30000);
if (mButtonDataEnabled .isChecked() &&
isNeedtoShowRoamingMsg()) {
mExtension.showWarningDlg( this,R.string.data_conn_under_roaming_hint);
}
///M: add for ATT requirement
mExtension.disableDataRoaming( mButtonDataRoam , mButtonDataEnabled.isChecked());
}
}
return true ;
}
…….
}

mConnService .setMobileDataEnabled( mButtonDataEnabled .isChecked()); 这里mConnService是 ConnectivityManager的实例,从这个方法之后,数据的启用将进入到 framework层.根据是否check决定开启或关闭数据网络。

(2).ConnectivityManager 中的setMobileDataEnabled(boolean)的调用实际是 ConnectivityService中的SetMobileDataEnable 方法
public void setMobileDataEnabled( boolean enabled) {
try {
mService.setMobileDataEnabled(enabled);
} catch (RemoteException e) {
}
}

(3).ConnectivityService 才是实际的网络管理核心。(标识 MTK的部分是MTK 进行的修改,这里可以跳过不看)该方法中先通过 enforceChangePermission()对调用程序进行是否有改变网络状态的权限的检查,如果没有,应用程序将会抛出异常被终止掉。然后发送消息 EVENT_SET_MOBILE_DATA(Message.what), 该消息将被内部类 InternalHandler的handleMessgae() 方法进行处理。
(/frameworks/base/services/java/com/android/server/ConnectivityService.java)

public void setMobileDataEnabled( boolean enabled) {
// M: To check permission for Mobile Manager Service/Application. @{
if (FeatureOption .MTK_MOBILE_MANAGEMENT) {
if (enabled && !checkMoMSSubPermission(SubPermissions.CHANGE_NETWORK_STATE_ON)) {
Slog. e(TAG, “setMobileDataEnabled(” + enabled + “) is lack of permission CHANGE_NETWORK_STATE_ON”);
return;
}
}
// @}
enforceChangePermission();
// MTK start
if (FeatureOption.MTK_GEMINI_SUPPORT) {
int curSlotId = Settings.System.getInt( mContext.getContentResolver(), Settings.System.GPRS_CONNECTION_SETTING , Settings.System.GPRS_CONNECTION_SETTING_DEFAULT ) - 1;
Log. e(“SJ”, “setMobileDataEnabled(” + enabled + “): curSlotId=” + curSlotId);
if (DBG ) Slog.d ( TAG, “setMobileDataEnabled(” + enabled + “): curSlotId=” + curSlotId);
if (enabled && (curSlotId == SimInfo. SLOT_NONE)) {
try{
mITelephony = getITelephony();
if( mITelephony == null){
Slog. e(TAG, “NULL in mITelephony”);
return;
}
for (int simId=PhoneConstants.GEMINI_SIM_1; simId

}

public boolean getAnyDataEnabled() {
final boolean result;
synchronized (mDataEnabledLock ) {
mUserDataEnabled = Settings.Global.getInt(
mPhone. getContext().getContentResolver(), Settings.Global. MOBILE_DATA, 1) == 1;
result = ( mInternalDataEnabled && sPolicyDataEnabled
&& ( mUserDataEnabled
|| ( mRequestedApnType.equals(PhoneConstants. APN_TYPE_MMS) && dataEnabled[DctConstants.APN_MMS_ID]))
&& ( enabledCount != 0));
}
if (!result && DBG ) log(“getAnyDataEnabled ” + result);
return result;
}
(9).调用onTrySetupData(String reason) 方法是DataConnectionTracker定义的抽象方法,将由其子类进行具体的实现。 DataConnectionTracker有2 个子类,分别是: GsmDataConnectionTracker和CdmaDataConnectionTracker 。这里将根据 sim支持的网络制式进行分支了。
(frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java)
(frameworks/opt/telephony/src/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java )

 GsmDataConnectionTracker 中的实现如下 :
 protected boolean onTrySetupData(String reason) {
    if (DBG ) log("onTrySetupData: reason=" + reason);     
    setupDataOnReadyApns(reason);
    return true ;
}
 CdmaDataConnectionTracker中实现如下:

protected boolean onTrySetupData(String reason) {
return trySetupData(reason);
}
两个分支中都是又调了其他的方法。 GsmDataConnectionTracker比CdmaDataConnectionTracker 多调了setupDataOnReadyApns方法,之后也会调用 trySetupdata()方法。

(10).先来看GsmDataConnectionTracker 分支,setupDataOnReadyApns(String reason)方法:根据当中的注释可知:这里循环遍历 mDataConnectionAsyncChannels 停止所有的数据链接的重连尝试闹钟。此后再一个遍历将 apnContext重置状态为idle ,以便能够执行到 trySetupdata()方法。
private void setupDataOnReadyApns (String reason) {
// Stop reconnect alarms on all data connections pending
// retry. Reset ApnContext state to IDLE.
for (DataConnectionAc dcac : mDataConnectionAsyncChannels .values()) {
if (dcac.getReconnectIntentSync() != null) {
cancelReconnectAlarm(dcac);
}
// update retry config for existing calls to match up
// ones for the new RAT.
if (dcac.dataConnection != null ) {
Collection apns = dcac.getApnListSync();

            boolean hasDefault = false ;
            for (ApnContext apnContext : apns) {
                if (apnContext.getApnType().equals(PhoneConstants. APN_TYPE_DEFAULT)) {
                    hasDefault = true;
                    break;
                }
            }
            configureRetry(dcac. dataConnection, hasDefault, 0);
        }
    }
    // Be sure retry counts for Apncontexts and DC's are sync'd.
    // When DCT/ApnContexts are refactored and we cleanup retrying
    // this won't be needed.
    resetAllRetryCounts();
    // Only check for default APN state
    for (ApnContext apnContext : mApnContexts .values()) {
        if (apnContext.getState() == DctConstants.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(DctConstants.State. IDLE);
        }
        if (apnContext.isReady()) {
            if (apnContext.getState() == DctConstants.State. IDLE ||
                    apnContext.getState() == DctConstants.State. SCANNING) {
                            Log. e("SJ", "setupDataOnReadyApns-->apnType" +apnContext.getApnType()+ "apnContext "+apnContext.toString()); 
                            apnContext.setReason(reason);
                            Log. e("SJ", "then enter trySetupData()" );
                trySetupData(apnContext);
            }
        }
    }
}

(11).Gsm的trySetupData 看起来感觉非常复杂。如果代码是在模拟器上的话,这里就更新 apnContext的连接状态为Connected,并且通过 mPhone提示数据连接已经改变。之后若成功的话将调用 setupData(apnContext);
private boolean trySetupData(ApnContext apnContext) {
String apnType = apnContext.getApnType();

    if (mPhone .getSimulatedRadioControl() != null ) {             
        // Assume data is connected on the simulator
        // FIXME  this can be improved
        apnContext.setState(DctConstants.State. CONNECTED);
        mPhone.notifyDataConnection(apnContext.getReason(), apnType);        
        return true ;
    }
    //MTK begin
    if( FeatureOption.MTK_GEMINI_SUPPORT && PhoneConstants. APN_TYPE_DEFAULT .equals(apnType)){
        int gprsDefaultSIM = getDataConnectionFromSetting();
        GeminiPhone mGeminiPhone = (GeminiPhone)PhoneFactory.getDefaultPhone ();                                               
        logd( "gprsDefaultSIM:" + gprsDefaultSIM);
        if(gprsDefaultSIM != mGsmPhone .getMySimId()){
              logd( "The setting is off(1)" );
              Log. e("SJ", "MTK stoped it,gprsDefaultSIM!=mGsmPhone.getMySimId" );
              return false ;
        } else if (gprsDefaultSIM < 0){
           logd( "The setting is off(2)" );
           Log. e("SJ", "MTK stoped it,gprsDefaultSIM<0" );
           return false ;
        } else if ( mGeminiPhone != null && mGeminiPhone.isGprsDetachingOrDetached( mGsmPhone .getMySimId()) &&
                !TextUtils. equals(apnContext.getReason(), Phone.REASON_DATA_ATTACHED)) {
            logd( "trySetupData: detaching or detached state." );
            Log. e("SJ", "MTK stoped it,mGeminiPhone  " );
            return false ;
        }
     }
    //MTK end
    if (apnContext.getState() == DctConstants.State. DISCONNECTING) {        
        apnContext.setReactive( true);
    }
    boolean desiredPowerState = mPhone .getServiceStateTracker().getDesiredPowerState();
    boolean anyDataEnabled = (FeatureOption.MTK_BSP_PACKAGE ||
            !isDataAllowedAsOff(apnType))? getAnyDataEnabled() : isNotDefaultTypeDataEnabled();             
    if ((apnContext.getState() == DctConstants.State. IDLE ||
            apnContext.getState() == DctConstants.State. SCANNING) &&
            isDataAllowed(apnContext) && anyDataEnabled && !isEmergency()) {

        if (apnContext.getState() == DctConstants.State. IDLE) {
            ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnType);
            if (waitingApns.isEmpty()) {
                if (DBG ) log("trySetupData: No APN found");        
                notifyNoData(GsmDataConnection.FailCause. MISSING_UNKNOWN_APN, apnContext);
                notifyOffApnsOfAvailability(apnContext.getReason());
                return false ;
            } else {
                apnContext.setWaitingApns(waitingApns);
                if (DBG ) {
                    log ( "trySetupData: Create from mAllApns : " + apnListToString( mAllApns));
                }
            }
        }
        if (DBG ) {
            log ( "Setup watingApns : " + apnListToString(apnContext.getWaitingApns()));
        }

        boolean retValue = setupData(apnContext);       
        notifyOffApnsOfAvailability(apnContext.getReason());
        return retValue;
    } else {
        // TODO: check the condition.            
        if (!apnContext.getApnType().equals(PhoneConstants. APN_TYPE_DEFAULT)
            && (apnContext.getState() == DctConstants.State. IDLE
                || apnContext.getState() == DctConstants.State. SCANNING))
            mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnType);
        notifyOffApnsOfAvailability(apnContext.getReason());
        return false ;
    }
}

(12).setupData()中将通过bringup 方法建立数据连接.这个方法相当复杂,没有仔细去研究,留待后面有时间再处理吧。
private boolean setupData(ApnContext apnContext) {
ApnSetting apn;
GsmDataConnection dc;
int profileId = getApnProfileID(apnContext.getApnType());
apn = apnContext.getNextWaitingApn();
if (apn == null ) {
if (DBG ) log(“setupData: return for no apn found!”);

        return false ;
    }  
    dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext);

    // M: check if the dc's APN setting is prefered APN for default connection.
    if (dc != null && PhoneConstants. APN_TYPE_DEFAULT.equals(apnContext.getApnType())) {
        ApnSetting dcApnSetting = dc.getApnSetting();
        if (dcApnSetting != null && !dcApnSetting. apn.equals(apn.apn )) {
            if (DBG ) log("The existing DC is not using prefered APN.");
            Log. e("SJ", "SetUpData-->The existing DC is not using prefered APN." );
            dc = null;
        }
    }

    if (dc == null ) {
        dc = findReadyDataConnection(apn);
        if (dc == null ) {

            if (DBG ) log("setupData: No ready GsmDataConnection found!");
            // TODO: When allocating you are mapping type to id. If more than 1 free,
            // then could findFreeDataConnection get the wrong one??
            dc = findFreeDataConnection();
        }

        if (dc == null ) {       
            dc = createDataConnection();
        }

        if (dc == null ) {          
            if (PhoneFactory.isDualTalkMode()) {
                //M: in dual-talk project, we only have single pdp ability.
                if (apnContext.getApnType() == PhoneConstants. APN_TYPE_DEFAULT)
                {                             
                    if (DBG ) log("setupData: No free GsmDataConnection found!");
                    return false ;
                }
                    ApnContext DisableapnContext = mApnContexts.get(PhoneConstants.APN_TYPE_DEFAULT);
                    clearWaitingApn();
                    cleanUpConnection( true, DisableapnContext);
                    //disableApnType(PhoneConstants.APN_TYPE_DEFAULT);
                    mWaitingApnList.add(apnContext.getApnType());
                    return true ;
                }
            } else {                              
                if (DBG ) log("setupData: No free GsmDataConnection found!");
                return false ;
            }
        }
    } else {
        apn = mDataConnectionAsyncChannels.get(dc.getDataConnectionId()).getApnSettingSync();
    }
    DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
    dc.setProfileId( profileId );  //  assumed no connection sharing on profiled types

    int refCount = dcac.getRefCountSync();
    if (DBG ) log("setupData: init dc and apnContext refCount=" + refCount);

    // configure retry count if no other Apn is using the same connection.
    if (refCount == 0) {
        configureRetry(dc, apn.canHandleType(PhoneConstants. APN_TYPE_DEFAULT),
                apnContext.getRetryCount());
    }

    if (apnContext.getDataConnectionAc() != null && apnContext.getDataConnectionAc() != dcac) {
        if (DBG ) log("setupData: dcac not null and not equal to assigned dcac.");
        apnContext.setDataConnectionAc( null);
    }

    apnContext.setDataConnectionAc(dcac);
    apnContext.setDataConnection(dc);

    apnContext.setApnSetting(apn);
    apnContext.setState(DctConstants.State. INITING);
    mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
    // If reconnect alarm is active on this DataConnection, wait for the alarm being
    // fired so that we don't disruppt data retry pattern engaged.
    if (apnContext.getDataConnectionAc().getReconnectIntentSync() != null) {
        if (DBG ) log("setupData: data reconnection pending");    
        apnContext.setState(DctConstants.State. FAILED);
        if (PhoneConstants.APN_TYPE_MMS.equals(apnContext.getApnType())) {
            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType(), PhoneConstants.DataState.CONNECTING );
        } else {
            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
        }
        return true ;
    }
    if (apnContext.getApnType() == PhoneConstants. APN_TYPE_MMS) {
        mWaitingApnList.clear();

        /**
         * M: if MMS's proxy IP address is the same as current connected interface
         *    and their APN is not the same, try to disable the existed one.
         *    Then, setup MMS's interface.
         */
        for (ApnContext currApnCtx : mApnContexts .values()) {
            ApnSetting apnSetting = currApnCtx.getApnSetting();

            if (currApnCtx == apnContext)
                continue;          
            if ((apnSetting != null ) && !currApnCtx.isDisconnected() &&
                        !apnSetting.equals(apn) && (isSameProxy(apnSetting, apn) && !apnSetting.apn.equals(apn.apn ))) {
                if (DBG ) logd("setupData: disable conflict APN " + currApnCtx.getApnType());
                disableApnType(currApnCtx.getApnType());
                mWaitingApnList.add(currApnCtx.getApnType());
            }
        }
    }

    Message msg = obtainMessage();    
    msg. what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
    msg. obj = apnContext;   
    dc.bringUp(msg, apn);

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

(13).再回过头来看看CdmaDataConnectionTracker的 trySetupData()方法。与Gsm 相同,这里也处理了模拟器的连接状态处理。之后也会调用 setupData(String reason),此后调用notifyoffApnsOfAvailability方法通知一些 apn的连接状态是DISCONNECTED,在 notifyoffApnsOfAvailability方法中循环遍历所有的apn,通过 isApnIdEnabled(int id)将所有未启用的apn筛选出来并利用 avail/unavail notificiations(notifyApnIdDisconnected方法 )送出去。—-当然这里为什么要发出去,目的是什么有什么作用,我还不太能理解清楚。
private boolean trySetupData(String reason) {
if (DBG ) log(“***trySetupData due to ” + (reason == null ? “(unspecified)” : reason));
if (mPhone .getSimulatedRadioControl() != null ) {
// Assume data is connected on the simulator
// FIXME this can be improved
setState(DctConstants.State. CONNECTED);
notifyDataConnection(reason);
notifyOffApnsOfAvailability(reason);

        log( "(fix?) We're on the simulator; assuming data is connected" );
        return true ;
    }

    int psState = mCdmaPhone .mSST .getCurrentDataConnectionState();
    boolean roaming = mPhone .getServiceState().getRoaming();
    boolean desiredPowerState = mCdmaPhone .mSST .getDesiredPowerState();

    if ((mState == DctConstants.State.IDLE || mState == DctConstants.State.SCANNING) &&
            isDataAllowed() && getAnyDataEnabled() && !isEmergency()) {
        boolean retValue = setupData(reason);
        notifyOffApnsOfAvailability(reason);
        return retValue;
    } else {
        notifyOffApnsOfAvailability(reason);
        return false ;
    }

}

protected void notifyOffApnsOfAvailability(String reason) {
if (DBG ) log(“notifyOffApnsOfAvailability - reason= ” + reason);
for (int id = 0; id < DctConstants.APN_NUM_TYPES; id++) {
if (id == apnTypeToId(apnIdToType(id)) && !isApnIdEnabled(id)) {
notifyApnIdDisconnected(reason, id);// 通知该apn的状态是断开的
Log. e(“SJ”, “notify disconnected–> id ” +id+” reason “+reason);
}
}

}

// since we normally don’t send info to a disconnected APN, we need to do this specially
//通常我们不会给一个断开的 apn发送消息,因此我们需要专门来做这个
private void notifyApnIdDisconnected(String reason, int apnId) {
mPhone.notifyDataConnection(reason, apnIdToType(apnId), PhoneConstants.DataState.DISCONNECTED );
}

(14).接下来在看一下CdmaDataConnectionTracker的 setupData(reason)方法。和gsm 相同,都是寻找对应的 DataConnection再根据其bringup() 方法建立数据连接,并带上了 DctConstants. EVENT_DATA_SETUP_COMPLETE 的消息。注意:当该动作完成后才被回调该消息

 private boolean setupData(String reason) {
        CdmaDataConnection conn = findFreeDataConnection();
        if (conn == null ) {
            if (DBG ) log("setupData: No free CdmaDataConnection found!");
            return false ;
        }
        /** TODO: We probably want the connection being setup to a parameter passed around */
        mPendingDataConnection = conn;
        String[] types;
        int apnId;
        if (mRequestedApnType .equals(PhoneConstants.APN_TYPE_DUN)) {
            types = mDunApnTypes;
            apnId = DctConstants. APN_DUN_ID;
        } else {
            types = mDefaultApnTypes;
            apnId = mDefaultApnId;
        }
        mActiveApn = new ApnSetting(apnId, "", "", "" , "", "", "" , "" , "" , "" ,
                                    "", 0, types, "IP" , "IP" , true , 0);
        if (DBG ) log("call conn.bringUp mActiveApn=" + mActiveApn );

        Message msg = obtainMessage();
        msg. what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
        msg. obj = reason;
        conn.bringUp(msg, mActiveApn);

        setState(DctConstants.State. INITING);
        notifyDataConnection(reason);
        return true ;
    }

(15).在GsmDataConnectionTracker 和CdmaDataConnectionTracker的 setup之后,回调到DataConnection的 bringUp方法。它将之前标记what为 DctConstants. EVENT_DATA_SETUP_COMPLETE 的消息和传递过来的 apnSetting实例构造成新的连接参数 ConnectionParams并发连接消息让DataConnection状态机对应的状态进行处理。 ConnectionParams是在 DataConnection的内部类,用来保存连接的参数信息。

     public void bringUp (Message onCompletedMsg, ApnSetting apn) {        
                sendMessage(obtainMessage( EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
    }

    /**
     * Used internally for saving connecting parameters.
     */
    protected static class ConnectionParams {
        public ConnectionParams(ApnSetting apn, Message onCompletedMsg) {
            this. apn = apn;
            this. onCompletedMsg = onCompletedMsg;
        }
        public int tag ;//这个tag 还不知道有什么作用
        public ApnSetting apn ;
        public Message onCompletedMsg ;
    }

(16).DataConnection 继承了StateMachine,关于状态机可以参考: http://blog.csdn.net/wsb1321/article/details/8021620
DataConnection一共有6 个状态,比起 wifi的状态要少很多,结构也相对简单很多。看一下其结构图:
(frameworks/opt/telephony/src/java/com/android/internal/telephony/DataConnection.java)

上面图结构根据构造方法可得.根据 setInitialState( mInactiveState )可知mInactiveState 为状态机初始状态

 protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm,
            DataConnectionTracker dct) {
        super(name);
        setLogRecSize(100);
        if (DBG ) log("DataConnection constructor E");
        this. phone = phone;
        this. mDataConnectionTracker = dct;
        mId = id;
        mRetryMgr = rm;
        this. cid = -1;
        setDbg( false);
        addState( mDefaultState);
            addState( mInactiveState, mDefaultState);
            addState( mActivatingState, mDefaultState);
            addState( mActiveState, mDefaultState);
            addState( mDisconnectingState, mDefaultState);
            addState( mDisconnectingErrorCreatingConnection , mDefaultState );
        setInitialState( mInactiveState);// 设置初始状态
        mApnList = new ArrayList<ApnContext>();
        if (DBG ) log("DataConnection constructor X");
    }

(17).mInactiveState为初始状态,因此当bringUp()调用时,发过来的EVENT_CONNECT消息将被它处理。看它的 processMessage方法:

@Override
        public boolean processMessage (Message msg) {
            boolean retVal;
......
  case EVENT_CONNECT :
                    ConnectionParams cp = (ConnectionParams) msg. obj;
                    cp. tag = mTag ;//首次进入的话, mTag值应该为1
                    if (DBG ) {
                        log( "DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
                                + mRefCount);
                    }
                    mRefCount = 1;            
                    onConnect(cp);
                    transitionTo( mActivatingState);
                    retVal = HANDLED;
                    break;
.....
}

关于 mTag的作用不是很理解,整个 DataConnction中在 仅mInactiveState的 enter方法中进行了值修改。

 public void enter() {
            mTag += 1;
....
}

此后调用传入connectionParams到onConnect方法,该方法是一个抽象方法,由GsmDataConnection和CdmaDataConnection实现。
(/frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmDataConnection.java )
(frameworks/opt/telephony/src/java/com/android/internal/telephony/cdma/CdmaDataConnection.java )

(18).先看GsmDataConnection 的onConnect方法。该方法通过调用 setupDataCal()开始进行数据连接,并且将连接参数 cp通过EVENT_SETUP_DATA_CONNECTION_DONE 消息在AsyncResult.userObj返回出去。

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;
        }*/
        //since in APN Editor, the roaming protocol item is showed only the current phone type is CDMA
        //so here we align this design and use protocol even if it is roaming
        String 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, String. valueOf(mId + 1), msg);         
    }

(19).再看下CdmaDataConnection中的onConnect()方法。从做的处理来看与Gsm的几乎没什么差别,但在调用setupDataCall方法时,参数有所不同。setupDataCall(String radioTechnology, String profile,String apn, String user, String password, String authType, String protocol, Message result) ,cdma并未进行 apn,user ,password的三个参数的传递。它调用的 setupDataCall其实与GsmDataConnection 中的setupDataCall也不相同,不过呢最终 CdmaDataConnection的setupDataCall 方法最终调用的地方与 GsmDataConntion相同。

@Override
    protected void onConnect(ConnectionParams cp) {
        if (DBG ) log("CdmaDataConnection Connecting...");     
        mApn = cp.apn ;
        createTime = -1;
        lastFailTime = -1;
        lastFailCause = FailCause.NONE;
        int dataProfile;
        if ((cp.apn != null ) && (cp. apn .types .length > 0) && (cp. apn .types [0] != null ) &&
                (cp. apn. types [0].equals(PhoneConstants. APN_TYPE_DUN))) {
            if (DBG ) log("CdmaDataConnection using DUN");
            dataProfile = RILConstants. DATA_PROFILE_TETHERED;
        } else {
            dataProfile = RILConstants. DATA_PROFILE_DEFAULT;
        }
        // msg.obj will be returned in AsyncResult.userObj;
        Message msg = obtainMessage( EVENT_SETUP_DATA_CONNECTION_DONE , cp);
        msg. obj = cp;
        phone. mCM .setupDataCall(
                Integer. toString(getRilRadioTechnology(RILConstants. SETUP_DATA_TECH_CDMA)),
                Integer. toString(dataProfile),
                null, null , null ,
                Integer. toString(RILConstants.SETUP_DATA_AUTH_PAP_CHAP),
                RILConstants. SETUP_DATA_PROTOCOL_IP, msg);
    }

(20).setupDataCall方法的实现在 RIL.jvaa中
(frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java)
CdmaDataConnection 中调用的是下面这个方法:

public void setupDataCall(String radioTechnology, String profile, String apn,
            String user, String password, String authType, String protocol,
            Message result) {
        /* [Note by mtk01411] In original Android2.1 release: MAX PDP Connection is 1
        * request_cid is only allowed to set as "1" manually
        */                    
                String request_cid = "1";
        setupDataCall(radioTechnology, profile, apn, user, password, authType, protocol, request_cid, result);
    }

实际上只是为了增加一个 request_cid 而已,之后调用的其实和 GsmDataConnetion是一样的

 public void setupDataCall(String radioTechnology, String profile, String apn,
            String user, String password, String authType, String protocol, String requestCid,
            Message result) {
        RILRequest rr
                = RILRequest. obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
        /* [Note by mtk01411] Currently, MAX PDP Connection is 1: request_cid is only allowed to set as "1" manually
        * But for Multiple PDP Connecitons Feature:
        * PdpConnection.java connect() shoud invoke this 8-parms version's setupDataCall() directly
        * request_cid is obtained from the parameter paased into the setupDataCall()
        */

        /* [Note by mtk01411] Change from 6 to 7: Due to add one cid field as the last one parameter */
        rr. mp.writeInt(8);
        rr. mp.writeString(radioTechnology);
        rr. mp.writeString(profile);
        rr. mp.writeString(apn);
        rr. mp.writeString(user);
        rr. mp.writeString(password);
        rr. mp.writeString(authType);
        /* [Add by mtk01411]
        * MAX PDP Connection =1:Only cid string "1" is allowed for RILD
        * MAX PDP Connection =2:Only cid string "1" or "2" is allowed for RILD
        * MAX PDP Connection =3:Only cid string "1" or "2" or "3" is allowed for our RILD
        */
        rr. mp.writeString(protocol);
        rr. mp.writeString(requestCid);
        if (RILJ_LOGD ) riljLog(rr.serialString() + "> "
                + requestToString(rr.mRequest) + " radioTech=" + radioTechnology + " profile="
                + profile + " apn=" + apn + " user=" + user + " password="
                + password + " authType=" + authType + " protocol=" + protocol + " requestCid=" + requestCid);

        send(rr);// 发送请求,由 RIL的构造函数中创建的RILSender线程完成向 socket写入数据
    }

(21).之后send 就是发消息给内部类 RILSender,再调用 socket发送数据了,当然再往下就是 c部分了,那里面是不会有对数据网络本身的开启关闭进行的处理的。因此从应用到框架的数据开启过程就是这样的流程了。

 private void send(RILRequest rr) {
        Message msg; 
        boolean show = (requestToString (rr. mRequest).compareTo("LAST_CALL_FAIL_CAUSE" ) == 0);
        if (mSocket == null ) {   
            rr.onError( RADIO_NOT_AVAILABLE, null );
            rr.release();
            return;
        }
        msg = mSender.obtainMessage( EVENT_SEND, rr);    
        acquireWakeLock();

        msg.sendToTarget();    
    }

之后 RILSender这个内部类调用LocalSocket进行数据发送

 case EVENT_SEND:
                    /**
                     * mRequestMessagePending++ already happened for every
                     * EVENT_SEND, thus we must make sure
                     * mRequestMessagePending -- happens once and only once
                     */
                    boolean alreadySubtracted = false ;
                    try {
                        LocalSocket s;
                        s = mSocket;

                        //MTK-START [mtk04070][111121][ALPS00093395]MTK modified
                        if (s == null || radioTemporarilyUnavailable !=RADIO_TEMPSTATE_AVAILABLE) {
                            rr.onError( RADIO_NOT_AVAILABLE, null );
                            rr.release();// 错误则将 RILRequest放回池中
                            mRequestMessagesPending--;
                            alreadySubtracted = true;
                            return;
                        }
                         //RILRequest请求列表,整型序列号serial作为其 id标识。
                        //当 RILSender发送一个RIL 请求后,则将其添加到该列表中(若发送时出现异常则需再清除);
                        //当请求完成并得到回送的 response消息后,则将其移除
                        synchronized (mRequestsList ) {
                            mRequestsList.add(rr);
                        }

                        mRequestMessagesPending--;
                        alreadySubtracted = true;
                        //MTK-END [mtk04070][111121][ALPS00093395]MTK modified

                        byte[] data;
                         //mp 即为rr中的 parcel,它当中是戴写入数据的缓冲区,经过 marshall优化后在赋值给data
                        data = rr. mp.marshall();
                        rr. mp.recycle();
                        rr. mp = null ;

                        if (data.length > RIL_MAX_COMMAND_BYTES ) {
                            throw new RuntimeException(
                                    "Parcel larger than max bytes allowed! "
                                                          + data.length);
                        }

                        // parcel length in big endian
                        dataLength[0] = dataLength [1] = 0;
                        dataLength[2] = ( byte)((data.length >> 8) & 0xff);
                        dataLength[3] = ( byte)((data.length ) & 0xff);

                        //Log.v(LOG_TAG, "writing packet: " + data.length + " bytes");

                        s.getOutputStream().write( dataLength);// 写入scoket数据长度,不能超过 RIL_MAX_COMMAND_BYTES
                        s.getOutputStream().write(data);// 写数据给 rild
                    } catch (IOException ex) {
                        Log. e(LOG_TAG, "IOException", ex);
                         // 如果发送过程中出现异常,通过 findAndRemoveRequestFromList移除该次RILRequest
                        req = findAndRemoveRequestFromList(rr. mSerial);
                        // make sure this request has not already been handled,
                        // eg , if RILReceiver cleared the list.
                        if (req != null || !alreadySubtracted) {
                            rr.onError( RADIO_NOT_AVAILABLE, null );
                            rr.release();
                        }
                    } catch (RuntimeException exc) {
                        Log. e(LOG_TAG, "Uncaught exception ", exc);
                          //如果发送过程中出现异常,通过 findAndRemoveRequestFromList移除该次RILRequest
                        req = findAndRemoveRequestFromList(rr. mSerial);
                        // make sure this request has not already been handled,
                        // eg , if RILReceiver cleared the list.
                        if (req != null || !alreadySubtracted) {
                            rr.onError( GENERIC_FAILURE, null );
                            rr.release();
                        }
                    } finally {
                        // Note: We are "Done" only if there are no outstanding
                        // requests or replies. Thus this code path will only release
                        // the wake lock on errors.
                        releaseWakeLockIfDone();
                    }

                    //MTK-START [mtk04070][111121][ALPS00093395]MTK modified
                    if (!alreadySubtracted) {
                        mRequestMessagesPending--;
                    }
                    //MTK-END [mtk04070][111121][ALPS00093395]MTK modified
                    break;

由此可见一个 RIL请求(RILRequest) 执行时一个异步过程,调用者调用 RIL类的api 函数只是往 RILSender线程添加了一个消息就返回,然后线程在执行无限循环时将其写入到 socket中,并将RILRequest 添加到一个列表中,再将数据优化后通过 socket写给rild 。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值