網路應用,對於使用者來說,主要是搜尋,瀏覽網頁,發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程式。