引言:
這段時間手中的工作,正好好調試一款3g modem,於是乎就分析了一下Android Ril的代碼,做了些總結歸納,閱讀時可以先看前后兩段以及流程圖,這樣可能更容易把握;
知識在於分享,文檔中可能有些地方寫的不對或是不完善,希望各位朋友留言指正,大家相互學習;
轉載時請說明出處;
歡迎大家留言討論,大家共同進步。
RIL 架構分析:
上圖清楚的標識了ril在整個Android系統各層的表現形式,我們這里主要分析Ril(RIDL、librefrenece_ril.so、libril.so);
…/Hardware/ril/rild RILD的代碼實現,有main函數,作為ril層的入口點,常駐系統進程,負責與上下層交互
…/Hardware/ril/libril 負責與守護進程交互???
…/Hardware/ril/reference-ril/ Ril庫的實現,主要負責與modem進行交互
實現詳細分析:
從init.rc中service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyUSB1 -u /dev/ttyUSB2
可以知道,Android啟動時,系統會啟動一個與ril相關的service (ril-daemon),其入口命令為/system/bin/rild
(一)那么首先看看rild(/hardware/ril/rild/*);該目錄下有兩文件radiooptions.c、rild.c
- Radiooptions.c 看Makefile知道最終會被編譯成radiooptions二進制工具,放在/system/bin/下面,具體用法我在這里就不說了,我到終點里面執行一下,把他的help信息打出了,再詳細的就自己看吧,源碼不長,也不復雜。
# radiooptions
Usage: radiooptions [option] [extra_socket_args]
0 - RADIO_RESET,
1 - RADIO_OFF,
2 - UNSOL_NETWORK_STATE_CHANGE,
3 - QXDM_ENABLE,
4 - QXDM_DISABLE,
5 - RADIO_ON,
6 apn- SETUP_PDP apn,
7 - DEACTIVE_PDP,
8 number - DIAL_CALL number,
9 - ANSWER_CALL,
10 - END_CALL
- Rild.c分析
1)有main函數入口,帶參數輸入,其輸入參數就是最前面提到的init.rc啟動ril-daemon是帶的參數;當然如果仔細分析main函數代碼,會發現其有多種獲取參數的方法。
2)dlHandle = dlopen(rilLibPath, RTLD_NOW),打開動態庫(system/lib/libreference-ril.so)。
3)RIL_startEventLoop(),啟動監聽事件隊列(自己的理解),具體作用我們后面分析
4)dlsym(dlHandle, "RIL_Init"),獲取動態庫的RIL_Init
5)調用RIL_Init方法,並獲取到RIL_RadioFunctions類型的返回值,RIL_RadioFunctions結構體包含了ril需要用的的幾個重要函數的函數指針
typedef struct {
int version; /* set to RIL_VERSION */
RIL_RequestFunc onRequest;
RIL_RadioStateRequest onStateRequest;
RIL_Supports supports;
RIL_Cancel onCancel;
RIL_GetVersion getVersion;
} RIL_RadioFunctions;
注釋:這些函數的具體實現,見/system/ril/reference-ril/*中的具體實現(這部分也是ril中變化最大的一部分,模塊或是廠家不一樣,其內容也會有較大的不同)
6)RIL_register(funcs),funcs就是5)中提到的RIL_RadioFunctions類型的返回值(具體實現見/system/ril/libril);
-
- 傳遞幾大重要函數的函數指針
- RIL_startEventLoop,注意這里會有判斷語句,如果監聽事件隊列啟動,則會監聽事件隊列機制
-
- 獲取Socket(rild)和Socket(rild-debug)的listen句柄,並分別將其加入監聽事件隊列。
- RIL_startEventLoop、ril_event_loop、watch_table、time_table、pending_list、readFds;這些都是監聽事件隊列很重要的函數,這里分析起來有點拗口
1)ril_event_loop是監聽事件隊列的主循環,當調用RIL_startEventLoop成功以后,其就一直在循環了;
2)現在來看一下事件描述的結構體,理解了這個才能繼續后面的;
struct ril_event {
struct ril_event *next;
struct ril_event *prev;
int fd; //事件相關的句柄;比如現在這個事件是正當socket的,那么這個句柄就應該是一個socket句柄;還可以使串口等其他設備
int index;//在list中的index值
bool persist;//如果保持,則其回常駐watch_table,不會被從list中刪除
struct timeval timeout;//select等待超時的時間
ril_event_cb func; //回調處理事件函數,通俗說就是當監聽的fd有數據來或是select被處罰,則會在將此函數扔到pending_list中待執行
void *param; //回調帶的參數
};
3)說明幾個重要的隊列
watch_table :監聽事件隊列,土話就是一個ril_event數據類型的鏈表或者數組;
time_table:超時事件隊列,也就是說本來某某事件是在time_table里面的,表示正在被監聽,過了些時間,超時了還沒被促發,那么這個某某事件就被扔 pending_list里面, 待執行;
pending_list:待執行事件隊列;
readFds:所以監聽事件相關句柄的集合,土話就是一個數組,里面放着正在被監聽的事件的相關的fd;
4)監聽事件隊列的相關函數(其實就和鏈表操作差不多,什么插入鏈表操作,刪除鏈表操作啦)
ril_event_init:初始化個事件隊列
ril_event_set:初始化某某事件
ril_event_add:添加事件到watch_table中
ril_timer_add:添加事件到time_table中
ril_event_del:從watch_table中刪除某某事件,對了time_table不需要刪除函數,其事件到了,事件會自動被清除掉
5)event部分流程圖
(二)看看reference-ril(/hardware/ril/reference-ril/*),這里就是ril定制的部分,也是差異化最大的部分。
- Ril_Init (注:在Rild main函數中被調用,見上一節rild.c分析)---------在這里我們將其描述為:用戶自定義RIL初始化部分
1)首先我們關注一下結構圖RIL_RadioFunctions(也稱為回調函數),這里面包含了一系列的ril操作相關的函數指針,最終提供給RILD(通過RIL_register)調用,個函數的字面意思已經比較清楚這里就不多做解釋。
static const RIL_RadioFunctions s_callbacks = {
RIL_VERSION,
onRequest,
currentState,
onSupports,
onCancel,
getVersion
};
2)start_uevent_monitor(),現在大部分modem支持串口和usb口通訊,這里我們這要針對USB口,其底層驅動通過USB轉串口來實現modem和系統進行交互,而這里是新建一個USB監控線程,監控USB插入、拔出等消息,主要應用與外置modem的熱插拔功能。
3)這里還有一些輸入參數的處理,這些輸入參數主要來自Rild的模塊main函數的輸入參數
4)新建線程mainLoop,下面我們進入mainLoop分析,這里才是干實事的地方
-
- open (s_device_path, O_RDWR); 打開AT通訊文件接口,例如/dev/ttyUSB*
- at_open(fd, onUnsolicited);這個函數很重要,其建立了兩個線程一個是readerLoop,一個是pppcheckLoop;
readerLoop:用來監聽接收來自modem AT口發送過來的命令回事數據
readerLoop:用來處理ppp網絡撥號上網
另外注意這里onUnsolicited是個函數指針,其主要用來處理當readerLoop接收到數據時,會調用其做一些優先的判斷處理,主要是進行一些 事件的主動上報,例如時間更新、短消息、ril狀體變化等。
5)RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_DELAYINIT);前開了好些個線程,應該也差不多了,這里應該需要對modem進行一下初始化設置了(通過AT命令),詳見initializeCallback,很多模塊初始化系列AT 命令都放在這里處理。
- readerLoop
這里我把這個家伙拎出來再說說,主要這里是返回response給上層的驅動器;AT命令都是以\r\n或\n\r為分隔符,readloop是line驅動的,當讀到一行完整的相應其才會有返回,才會上報。
(三)請求流程(Java層是如何調用到ril里面來的,其實這也是reference-ril的一部分)
1)還記得在(一)中描述的RIL_startEventLoop-------->ril_event_loop------>監聽着一系列的nfds,其中這里就監聽了socket rild(見(一)中6)的描述),其 向ril_event_loop中就加入了RILD socket的listen event,當java層向此套接字發送連接請求時,其select就會監聽到socket的連接請求,則會調用event對應的處理函數listenCallback(),具體如何被調用請參看圖4 。
2)接下來看listenCallback()
……
s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen); //等待接收socket的連接請求,獲取socket連接符s_fdCommand
……
p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES); //建立一個record stream 與s_fdCommand綁定
ril_event_set (&s_commands_event, s_fdCommand, 1,processCommandsCallback, p_rs);//建立一個event,當s_fdCommand有數據,則調用回調 函數processCommandsCallback
rilEventAddWakeup (&s_commands_event); //向watch_table中插入event
3)接着分析processCommandsCallback()
其取到上層發送過來的數據后,會調用processCommandBuffer(p_record, recordlen) ,其中p_record包含上層發送過來的消息和數據
4) 接下來分析processCommandBuffer()
- 從java層傳遞下來的命令格式為:parcel+Request號+令牌+內容,這里Request號很重要,它們被定義在RILConstants.java和ril_command.h中,其中它們的定義是一致的;根據Request號我們可以找到對應的dispatchFunction函數和responseFunction函數,詳見ril_commands.h以及CommandInfo結構體:
static CommandInfo s_commands[] = {
#include "ril_commands.h"
};
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
- 從processCommandBuffer()中我們可以得知,在從java層傳遞的數據中提取出Request號找到對應的dispatchFunction函數,並調用對應的pRI->pCI->dispatchFunction(p, pRI)函數;
- 對應的dispatchFunction函數最終會調用s_callbacks.onRequest(pRI->pCI->requestNumber, xxx,sizeof(char *), pRI),這里的s_callbacks就是(二)----1)中所提到的回調函數指針,這些函數的最終實現是在reference-ril.cpp中,這里調用的是reference-ril中的onRequest()函數
5)所以總結下來請求流程最終都會調用reference-ril中的onRequest()函數,所以如果你不想了解那么多,就直接把onRequest函數當成請求流程的處理函數即可,當然如果你要增減一些對應Java層數據處理功能,那么這套流程你還是需要清楚的。
(四)Response流程(Ril接收到的數據是如何返回給上層的,這也是reference-ril的一部分)
1)記得前面提過readerLoop,其實在mainloop中開啟的一個線程,主要最用是監聽modem AT 通訊口,並且接受其發過來的數據,並進行分類處理,
這里的分類只的是普通AT 命令和短信/自動上報處理;
2)先分析短信處理,在readerloop()中通過readline()函數接收響應,然后經過isSMSUnsolicited檢測是否是短信/自動上報,如果是則通過 s_unsolHander(也就是函數onUnsolicited)來處理;如果不是短信/自動上報,則執行processLine來處理;
-
- onUnsolicited函數(短信/自動上報處理)最終會命令信息以及上報信息內容,可分成兩種操作:RIL_onUnsolicitedResponse和RIL_requestTimedCallback;
- RIL_onUnsolicitedResponse會將unsolicited信息直接返回給上層,調用流程為 RIL_onUnsolicitedResponse ---->sendResponse---->sendResponseRaw---->blockingWrite;
- blockingWrite(fd, (void *)&header, sizeof(header))表示想fd寫入數據,這里fd就是s_fdCommand即前面提到的和java層建立起來的socket,這里表示通過socke(RILD)將response信息傳遞給上層框架;
- RIL_requestTimedCallback通過event機制實現timer機制,回調對應的內部處理函數;
3)接着我們來看一下非短信/自動上報的處理過程,這里我們之間分析processLine函數,這個東東會把沖modem過來的response信息存儲到一個臨時變量sp_response中,具體怎么存儲的勞煩您還是去看看代碼吧,都是淚啊!
-
- 接下來咋們得分析at_send_command_full_nolock函數,這個函數就充分利用了sp_response這個變量的值,它最終會將sp_response返回給調用at_send_command_full_nolock的函數(時間就是存在於Ril_commands.h中以“request*”開頭的函數,其實這些函數最終都會調用到onRequest函數,給詳細的理解請回顧(三));
- 當取得sp_response(即response信息)后,則response信息可以通過RIL_onRequestComplete()函數返回給上層,最后這些信息也是通過soket(RILD)返回給應用的,和前面介紹的基本類似
總結歸納:
- 首先是RIL整體架構,從宏觀上了解一下下RIL
(一)主要是從init.rc、main函數這樣的程序員思維,先了解ril在Android中時怎么起來的,以及一部分的具體實現。
(二)(三)(四)才是RIl的核心部分,前面只是打基礎,他們分別說明了RIL如何初始化、如何接收消息和數據、如何返回消息和數據
- 接下來我們用圖表的形式來歸納一下ril初始化 以及"接收"和"返回"數據的過程: