網路應用,對於使用者來說,主要是搜尋,瀏覽網頁,發email,發mms。對於android來說這些應用涉及到的一些中間過程,主要涉及到webkit,wap,smtp等協議,然後是socket通訊,然後就是
linux核心的tcpip協議棧,及ppp協議,最後再是ttyS0之類的裝置介面,最後由modem發送資料。另外手機的網路介面可能不是modem,有可能是網卡或者wifi,那麼android中也有相應的介面可以添加。
不過有線網卡,目前用的比較少,畢竟手機或平板電腦上很少會用到體積大的網口。
先在總體上看一下網路流程
這裡主要討論modem的形式。
應用程式->觸發網路連接(或已串連)->android本地的jni socket函數->核心中的BSD socket?->tcp/ip->ppp->/dev/ttySx(modem的資料口)。
在觸發的網路連接,如果沒有串連,則會時行撥號,撥號有一些初始化at命令,及一個撥號命令,atd*99***1#,這些實現是RIL.java與相對應的ril.cpp檔案中完成。撥號成功後,就進行ppp協商過程,ppp協商成功後,移動網路會給終端分配ip地址,網關和dns地址。然後網路連接就成功了。之後就就是發送上層應用程式資料。
在我們的應用程式中,觸發了上網需求,系統會去檢測網路是否串連,當然對於有多種介面,會輪詢,看哪一個串連可用(這裡應該是否有優先順序,先有線,然後wifi,最後是modem,畢竟modem的費用是最高)
在ppp撥號之前,所有的實現都包含在PhoneService中,即在frameworks/base/telephony/java/com/android/internal/telephony/目錄下,
畢竟撥號這個動作還是屬於電話範疇。
首先在PhoneApp.java中:onCreate
PhoneFactory.makeDefaultPhones(this); //產生一個基本的電話語音形式
在PhoneFactory.java中的:public static void makeDefaultPhone(Context context)中
int phoneType = getPhoneType(networkMode);
if (phoneType == Phone.PHONE_TYPE_GSM) {
sProxyPhone = new PhoneProxy(new GSMPhone(context,
sCommandsInterface, sPhoneNotifier));
Log.i(LOG_TAG, "Creating GSMPhone");
} else if (phoneType == Phone.PHONE_TYPE_CDMA) {
sProxyPhone = new PhoneProxy(new CDMAPhone(context,
sCommandsInterface, sPhoneNotifier));
Log.i(LOG_TAG, "Creating CDMAPhone");
}
//建立PhoneProxy,則PhoneProxy中,又建立GSMPhone,如果網路是cdma,那麼則建立CDMAPhone。這裡不討論cdma制式的網路。
在GSMPhone.java的,建構函式中,mDataConnection = new GsmDataConnectionTracker (this);
在GsmDataConnectionTracker.java中,GsmDataConnectionTracker類是繼承於DataConnectionTracker。在GsmDataConnectionTracker建構函式調用了createAllPdpList();函數在
GsmDataConnectionTracker.java中。如下:
private void createAllPdpList() {
pdpList = new ArrayList<DataConnection>();
DataConnection pdp;
for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) {
pdp = new PdpConnection(mGsmPhone);
pdpList.add(pdp);
}
}
//建立了PDP_CONNECTION_POOL_SIZE個PdpConnection(PDP_CONNECTION_POOL_SIZE等於1)
在應用程式觸發網路發送資料,如觸發了onApnChanged,onRoamingOff, onRoamingOn等函數,或者處理訊息時,會調用trySetupData函數
對於trySetupData函數,剛會調用setupData(reason);進行資料連線。
private boolean setupData(String reason) {
ApnSetting apn;
PdpConnection pdp;
apn = getNextApn();
if (apn == null) return false;
pdp = findFreePdp();
if (pdp == null) {
if (DBG) log("setupData: No free PdpConnection found!");
return false;
}
mActiveApn = apn;
mActivePdp = pdp;
Message msg = obtainMessage();
msg.what = EVENT_DATA_SETUP_COMPLETE;
msg.obj = reason;
pdp.connect(apn, msg);
setState(State.INITING);
phone.notifyDataConnection(reason);
return true;
}
//調用了pdp.connect(apn, msg)。時行撥號,即應該是往RIL.java層發送撥號請求了。
接下來,看PdpConnection.java檔案,繼承於DataConnection
此檔案實現了connect,disconnect等方法。
在connect,setupDataCall介面方法
phone.mCM.setupDataCall(Integer.toString(RILConstants.SETUP_DATA_TECH_GSM),
Integer.toString(RILConstants.DATA_PROFILE_DEFAULT), apn.apn, apn.user,
apn.password, Integer.toString(authType),
obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE));
mCM的類型是CommandsInterface,即是一些電話語音相碰的通用介面。這個介面的實現,就是RIL類,下面看RIL.java
RIL.java完成android電話語音與modem操作的一轉換功能。即把一些電話語音轉換為實現的at命令,發送到modem
在RIL類的中setupDataCall方法,實現如下:
public void
setupDataCall(String radioTechnology, String profile, String apn,
String user, String password, String authType, Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
rr.mp.writeInt(6);
rr.mp.writeString(radioTechnology);
rr.mp.writeString(profile);
rr.mp.writeString(apn);
rr.mp.writeString(user);
rr.mp.writeString(password);
rr.mp.writeString(authType);
if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ requestToString(rr.mRequest) + " " + radioTechnology + " "
+ profile + " " + apn + " " + user + " "
+ password + " " + authType);
send(rr);
}
這個是通過socket方式給ril.cpp發送相關請求,引處發送RIL_REQUEST_SETUP_DATA_CALL請求,那麼在ril.cpp就會相應的完成話往modem通過串口發送ATD*99***1#命令。最後回返at命令返回結果,成功的話,就會返回CONNECT OK,這時ril.cpp就會往RIL.java發送此命令成功的響應。在RIL.java中的run函數的迴圈中調用processResponse方法進行解析相關的響應。
processResponse()->processSolicited ()->case RIL_REQUEST_SETUP_DATA_CALL: ret = responseStrings(p); break;再通過rr.mResult.sendToTarget(),把返回結果送到上一層模組中。
此時,系統會調用ppp撥號程式ppp的撥號原始碼在external/ppp目錄下。
在Android1.6版本之前,系統封裝了調用pppd的代碼,在frameworks/base/telephony/java/com/android/internal/telephony/PppLink.java中。
但之後就沒有了,網上的介紹說是用了高通的方案,把pppd與系統的進程的通訊通過了記憶體共用的方式來實現,因此在代碼中就去掉了調用pppd的java代碼。
如何共用?這是個問題。不過應該是系統的事了。
那麼自己要手動進行pppd撥號了。
什麼時候撥號?這個是關鍵。gprs畢竟是有流量。大家都比較喜歡按需撥號,相當於智能撥號了。ppp已經提供了這個功能,加相應的參數即可。
不過在新的android版本中,已經ppp代碼進行了簡化,沒有chat.c檔案,那自然沒得有chat命令,也就無法用pppd call xxx這個命令。如果想用chat那麼就要自己移植ppp程式。