Android RIL結構分析與移植

Android RIL結構分析與移植

介紹java

本文檔對Android RIL部分的內容進行了介紹,其重點放在了Android RIL的原生代碼部分。android

包括四個主題:windows

1.Android RIL框架介紹數組

2.Android RIL與 WindowsMobile RIL網絡

3.Android RIL portingapp

4.Android RIL的java框架框架

 

在本文檔中將Android代碼中的重要模塊列出進行分析,並給出了相關的程序執行流程介紹,以加深對模塊間交互方式的理解。異步

對於java代碼部分,這裏僅進行簡單的介紹。若是須要深刻了解,能夠查看相關參考資料。socket

本文檔中還對Android RIL的Porting部份內容進行了描述和分析。函數

 

針對對unix操做系統環境並不熟悉的讀者,本文檔中所涉及到的相關知識包括:


Unix file system


Unix socket


Unix thread


Unix 下I/O多路轉接

 

以上信息能夠在任意一份描述Unix系統調用的文檔中找到。

 

1.Android RIL框架介紹

術語:

fd
unix文件描述符

pipe
unix管道

cond
通常是condition variable的縮寫

tty
一般使用tty來簡稱各類類型的終端設備

unsolicited response
被動請求命令來自baseband

event loop
android的消息隊列機制,由unix的系統調用select()實現

init.rc
init守護進程啓動後被執行的啓動腳本。

HAL
硬件抽象層(Hardware Abstraction Layer,HAL)

 

1.1.Android RIL概況:

 

Android RIL提供了無線硬件設備與電話服務之間的抽象層。

下圖展現了RIL在Android體系中的位置。

 


android的ril位於應用程序框架與內核之間,分紅了兩個部分,一個部分是rild,它負責socket與應用程序框架進行通訊。另一個部分是Vendor RIL,這個部分負責向下是經過兩種方式與radio進行通訊,它們是直接與radio通訊的AT指令通道和用於傳輸包數據的通道,數據通道用於手機的上網功能。

對於RIL的java框架部分,也被分紅了兩個部分,一個是RIL模塊,這個模塊主要用於與下層的rild進行通訊,另一個是Phone模塊,這個模塊直接暴露電話功能接口給應用開發用戶,供他們調用以進行電話功能的實現。

 

 

 

1.2.Android RIL目錄結構:

Android的RIL模塊位於Android/hardware/ril文件夾,有三個子模塊:rild , libril , reference-ril

●include文件夾:

包含RIL頭文件,最主要的是ril.h

●rild文件夾:

RIL守護進程,開機時被init守護進程調用啓動,裏面僅有main函數做爲入口點,負責完成RIL初始化工做。

在rild.c文件中,將完成ril的加載過程,它會執行以下操做:


動態加載Vendor RIL的.so文件


執行RIL_startEventLoop()開啓消息隊列以進行事件監聽


經過執行Vendor RIL的rilInit()方法來進行Vendor RIL與libril的關係創建。

在rild文件夾中還包括一個radiooptions.c文件,它的做用是經過串口將一些radio相關的參數直接傳給rild來對radio進行配置。

●libril文件夾:

在編譯時libril被鏈入rild,它爲rild提供了event處理功能,還提供了在rild與Vendor RIL之間傳遞請求和響應消息的能力。

Libril提供的主要功能分佈在兩個主要方法內,一個是RIL_startEventLoop()方法,另外一個是RIL_register()方法

RIL_startEventLoop()方法所提供的功能就是啓用eventLoop線程,開始執行RIL消息隊列。

RIL_register()方法的主要功能是啓動名爲 rild 的監聽端口,等待java 端經過socket進行鏈接。

●reference-ril文件夾:

Android自帶的Vendor RIL的參考實現。被編譯成.so文件,因爲本部分是廠商定製的重點所在。因此被設計爲鬆散耦合,且可靈活配置的。在rild中經過opendl()的方式加載。

librefrence.so負責直接與radio通訊,這包括未來自libril的指令轉換爲AT指令,而且將AT指令寫入radio中。

reference-ril會接收調用者傳來的參數,參數內容爲與radio的通訊方式。如經過串口鏈接radio,那麼參數爲這種形式:-d /dev/ttySx

 

1.3.Android RIL中的消息(event)隊列機制:

在Android RIL中,爲了達到等待多路輸入而且不出現阻塞的目的,使用了IO多路複用機制。

若是使用阻塞I/O進行網絡的讀取寫入,這意味着假如須要同時從兩個網絡文件描述符中讀內容,那麼若是讀取操做在等待網絡數據到來,這將可能很長時間阻塞在一個描述符上,另外一個網絡文件描述符無論有沒有數據到來都沒法被讀取。

一種解決方案是:

若是使用非阻塞I/O進行網絡的讀取寫入,在讀取其中一個網絡文件描述符若是阻塞將直接返回,再讀取另一個,這種方式的循環被稱之爲輪詢。輪詢方式確實能解決進行多路io操做時的阻塞問題,可是這種方法的不足之處是反覆的執行讀寫調用將浪費cpu時鐘。

 

I/O多路轉接技術在這裏提供了另外一種比較好的解決方案:

它會先構造一張有關I/O描述符的列表,而後調用select函數,當IO描述符列表中的一個描述符準備好進行I/O時,該函數返回,並告知能夠讀或寫哪一個描述符。

Android RIL中消息隊列的核心實現思想就是這種I/O多路轉接技術。

消息隊列機制的實如今ril_event.cpp中,其中被定義的ril_event結構是消息的主體。

每一個ril_event結構,與一個fd句柄綁定(能夠是文件,socket,管道等),而且帶一個func指針,這個func指針所指的函數是個回調函數,它指定了當所綁定的fd準備好進行讀取時所要進行的操做。

消息隊列的開始點爲RIL_startEventLoop函數。RIL_startEventLoop在ril.cpp中實現,它的主要目的是經過pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL)創建一個dispatch線程,線程入口點在eventLoop. 而在eventLoop中,會調ril_event.cpp中的ril_event_loop()函數,創建起消息隊列機制。

ril_event是一個帶有鏈表行爲的struct,它最主要的成員一個是fd,一個是func:

[c-sharp] view plaincopyprint?

  1. struct ril_event {  

  2.     struct ril_event *next;  

  3.     struct ril_event *prev;  

  4.     int fd;  

  5.     int index;  

  6.     bool persist;  

  7.     struct timeval timeout;  

  8.     ril_event_cb func;  

  9.     void *param;  

  10. };  

struct ril_event {    struct ril_event *next;    struct ril_event *prev;    int fd;    int index;    bool persist;    struct timeval timeout;    ril_event_cb func;    void *param; }; 

初始化一個新ril_event的操做是經過ril_event_set()來完成的,並經過ril_event_add()加入到消息隊列之中,add會把隊列裏全部ril_event的fd,放入一個fd集合readFds中。這樣 ril_event_loop能經過一個多路複用I/O的機制(select)來等待這些fd。

在進入ril_event_loop()以前,在eventLoop中已經建立和掛入了s_wakeupfd_event,它是經過pipe的機制實現的,這個管道fd的回調函數並無實現什麼功能,它的目的只是爲了讓select方法能返回一次,這樣select()方法就能從新跟蹤新加入事件隊列的fd和timeout設置。

因此在添加新fd到eventLoop時,每每不是直接調用ril_event_add,實際一般用rilEventAddWakeup來添加,這個方法除了會間接調用ril_event_add外,還會調用triggerEvLoop()函數來向s_fdWakeupWrite中寫入一個空字符,這樣select()函數會返回並從新執行,新加入的文件描述符便得以被select()加載並跟蹤。

若是在ril_event隊列中任何一個fd已經準備好,則進入分析流程:

processTimeouts(),processReadReadies(&rfds, n),firePending().其中firePending()方法執行這個event的func,也就是回調函數。

 

在Android RIL初始化完成後,將有幾個event被掛入到eventLoop中:

1.s_listen_event: 名爲rild的socket,主要requeset & response通道

2.s_debug_event: 名爲rild-debug的socket,調試用requeset & response通道

3.s_wakeupfd_event: 無名管道,用於隊列主動喚醒

這其中最爲重要的event就是s_listen_event,它做爲request與response的通道實現。

在ril_event.cpp中還持有一個watch_table數組,一個timer_list鏈表和一個pending_list鏈表。

watch_table數組的目的很單純,存放當前被eventLoop等待的ril_event(非timer event),供eventLoop喚醒時使用。

timer_list是存放timer event的鏈表,在eventLoop喚醒時要對這些timer event單獨進行處理

pending_list:待處理(對其回調函數進行調用)的全部ril_event的鏈表。


1.4.Android RIL中初始化流程分析:

●Rild的初始化流程

初始化流程從rild.c中的main函數開始,它被init守護進行調用執行:

首先在main()函數內會首先經過dlopen()函數加載Vendor RIL(在自帶的參考實現中爲librefrence_ril.so)。接着調用RIL_startEventLoop()函數來啓動消息隊列機制。

調用librefrence_ril.so的RIL_Init()函數來進行Vendor RIL的初始化。RIL_Init()函數執行後會返回一個RIL_RadioFunction結構體,這個結構體內最重要的成員就是onRequest()方法。onRequest()方法會被dispatchFunction調用,也就是說dispatchFunction調用是程序流從rild轉入Vendor RIL的分界點。

RIL_register()函數將實現兩個目地,一個是將RIL_INIT中得到的RIL_RadioFunction進行註冊,rild經過此種方式保證本身持有一個RIL_RadioFunction實例,第二個是將s_fdListen加入到消息隊列機制中,開啓s_fdListen的事件監聽。

●Vendor RIL的初始化流程:

RIL_Init被調用後首先經過參數獲取硬件接口的設備文件或模擬硬件接口的socket。(參見上文中對reference-ril文件夾的介紹)

接下來是建立mainLoop線程,並跳入到線程內執行。mainLoop會創建起與硬件的通訊,而後經過read方法阻塞等待硬件的主動上報或響應。mainLoop還會調用initlizeCallBack()函數來向radio發送一系列的AT命令來進行radio的初始化設置工做。

 

1.5.Android RIL中request流程分析:

上層應用開始向rild經過socket傳輸數據時,經過RIL消息隊列機制,s_listen_event的回調函數listenCallBack將會被調用,開始進行數據流的分析與處理。

接下來,s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen),獲取傳入的socket描述符,也就是上層的java RIL傳入的鏈接。

而後,經過record_stream_new()創建起一個RecordStream, 將這個record_stream與s_fdCommand綁定, RecordStream其實是一個用於存放數據的結構體,這個結構體提供了一些操做類來保證這個RecordStream所綁定的文件描述符被讀取時裏面的數據會被完整讀取。

一旦s_fdCommand中有數據,它的回調函數processCommandsCallback()將會被調用,processCommandsCallback()經過record_stream_get_next阻塞讀取s_fdCommand上發來的數據, 直到收到一完整的request。而後將其傳遞進processCommandBuffer()函數,processCommandBuffer()正式進入了命令的解析部分。

每一個接收到的命令將以RequestInfo的形式存在。從socket過來的數據流,是Parcel處理過的序列化字節流, 在這裏會經過反序列化的方法提取出來。最前面的是request號, 以及token域(request的遞增序列號)。request號是一個CommandInfo,它在ril_command.h中定義。

接下來, 這個RequestInfo會被掛入pending的request隊列, 執行具體的dispatchFunction(), 進行詳細解析。

dispatchFunction方法有着多種實現,如dispatchVoid, dispatchString, 它們的調用取決於Parcel的參數傳入形式。好比說在dispatchDial方法中,Parcel對象將被解析爲RIL_Dial結構。這是disptachFunction的任務之一,它的另外一個任務就是調用onRequest()方法,並將解析的內容傳入onRequest()方法。

從onRequest方法開始,程序控制流脫離了RILD,進入到了Vendor RIL中。

onRequest方法會經過傳入的請求類型來調用指定的request×××()方法,request×××()方法則負責組裝AT指令並下發給at_send_command()方法集合中的一個,這個方法集合提供了針對不一樣類型AT指令的實現,如單行AT指令at_send_command_singleline(),短信息指令at_send_command_sms()等。

最後,執行at_send_command_full(),再經過一個互斥的at_send_command_full_nolock()調用,完成最終的寫出操做,在writeline()中,寫出到初始化時打開的設備中。

須要注意的是:at_send_command_full_nolock()在將指令寫入radio後並不會直接返回,而是經過條件變量等待響應信息,獲得響應信息後會攜帶這些信息返回。具體流程能夠參考下面的response流程分析。

 

1.6.Android RIL中response流程分析:

AT的response有兩種,一種是unsolicited。另外一種是普通response,也就是命令的響應。

response信息的獲取在readerLoop()中。由readline()函數讀取上來。

讀取到的line將被傳入processLine()函數進行解析,processLine()函數首先會判斷當前的響應是主動響應仍是普通響應,若是是主動響應,將調用handleUnsolicited()函數,若是爲普通響應,那麼將調用handleFinalResponse()函數進行處理對響應串的主要的解析過程,由at_tok.c中的各類解析函數完成,提供字符串分析解析功能。

 

●對主動上報的解析

handleUnsolicited ()方法處理主動上報,它會調用onUnsolicited()來進行進一步的解析,這個函數在Vendor-RIL初始化時被傳入at_open()函數,onUnsolicited只解析出頭部(通常是+XXXX的形式),而後按類型決定下一步操做,操做爲 RIL_onUnsolicitedResponse和RIL_requestTimedCallback兩種。

在RIL_onUnsolicitedResponse()函數中,經過Parcel傳遞,將 RESPONSE_UNSOLICITED,unsolResponse(request號)寫入Parcel,而後調用對應的responseFunction完成進一步的的解析,將解析的數據寫入Parcel中,最後經過sendResponse()→sendResponseRaw()→blockingWrite()→writeLine()將數據寫回給與應用層通訊的socket。

在RIL_requestTimedCallback()函數中。經過event機制實現的timer機制,回調對應的內部處理函數。經過internalRequestTimedCallback將回調添加到event循環,最終完成callback上掛的函數的回調。好比 pollSIMState,onPDPContextListChanged等回調, 不用返回上層,內部處理就能夠。

●對普通上報的解析

IsFinalResponse()和isFinalResponseError()所處理的是一條AT指令的響應上報,它們將轉入handleFinalResponse方法。

handleFinalResponse()函數會將全部響應信息裝入sp_response,這是一個ATResponse結構,它的成員包括成功與否(success)以及一箇中間結果(p_intermediates)。

handleFinalResponse()在將響應結果保存至sp_response後, 設置s_commandcond這一條件變量,此條件變量由at_send_command_full_nolock等待。

at_send_command_full_nolock得到到了完整的響應信息(在sp_response中),便開始進行響應信息的處理,最後由RIL_onRequestComplete將響應數據序列化並經過sendResponse傳遞至與應用層通訊的socket,這一部分與RIL_onUnsolicitedResponse()函數的功能很是類似,請參考對主動上報的解析部分。

 

2.Android RIL與 WindowsMobile RIL

Android RIL與WindowsMobile RIL 在設計思路上都是做爲一個radio的抽象,爲上層提供電話服務,但在實現方式上二者有着必定的差別,這種差別的產生主要是源自操做系統機制的不一樣。

Android RIL被實現爲HAL,相對於windows mobile中被實現爲驅動的方式,Android RIL模塊的內聚性更爲理想,可維護性也將更強,你也能夠把Android Ril 看作一箇中間件。Android RIL部分的開發工做,只須要拿到相應的radio文件描述符,就能夠進行操做,無需關注radio的I/O驅動實現。

 

2.1二者在與應用通訊上的實現對比

WindowsMobile RIL在實現與應用的通訊時提供了RIL Proxy,在這個層面中它定義了大量的RIL_***()函數來做爲電話服務請求。這一點與Android RIL的實現比較類似,Android RIL中在ril.h內提供了一系列的宏來定義電話服務請求。

在Android中的rild功能相似於windows mobile RIL的RIL proxy。它一樣也是起到一箇中介的做用,爲上層接口向下傳遞請求,並上傳回響應。在windows mobile RIL中要爲每個應用程序客戶提供一份Ril Proxy實例。

對於這兩種操做系統平臺,RIL所定義的全部請求是不可更改的。


2.2二者在線程結構與回調機制上的對比

在windows mobile的設計中,request與response被設計爲異步執行的,他們分別使用兩個隊列來對它們的異步行爲進行管理,執行命令下發和上報命令處理的過程也互不影響,下發命令與命令的相應響應之間的依賴關係由應用程序來捏合。

在android ril中的request與response設計與windows mobile不一樣,它的命令與響應之間是同步的過程。也就是說一條命令被下發後,將等待執行結果,並進行處理,再上向上層發。而不是直接異步的進行處理和向上發送。

3.Android RIL porting

3.1.命名

要實現某個無線模塊的RIL,須要建立一個實現了全部請求方法的共享庫,保證Android可以響應無線通訊請求。全部的請求被定義ril.h中。

不一樣的Modem使用不一樣的端口,這個在init.rc中設置。

  Android提供了一個參考Vendor RIL,RIL參考源碼在reference-ril。

  將你本身的Vendor RIL實現編譯爲共享庫形式:libril-<companyname>-<RIL version>.so

 

  好比:

  libril-techfaith-124.so

  其中:

   libril:全部vendor RIL的開頭

   <companyname>:公司縮寫

   <RIL version>:RIL版本number

   so:文件擴展

 

3.2.Android RIL的配置與加載

在init.rc文件中,將經過這種方式來進行Android RIL的加載。

service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyS0

也能夠手動加載:

/system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyS0

這兩種方式,都將啓動rild守護進程,而後經過-l參數將libreference-ril.so共享庫鏈入,libreference-ril.so的參數-d是指加載一個串口設備,/dev/ttyS0則是這個串口設備的具體設備文件,除了參數-d外,還有-s表明加載類型爲socket的設備,-p表明迴環接口。

 

3.3.Android RIL的編譯結構

rild:

被編譯成可執行文件,rild以守進程的形式執行。

libril:

將被編譯爲共享庫,並被鏈入rild。

Vendor RIL:

能夠以兩種方式來運行,若是定義了RIL_SHLIB宏,那麼它將被編譯成共享庫,若是沒定義RIL_SHLIB宏,它將以守護進程程序的方式被調用執行。

4.Android RIL的java框架

Android RIL的Java部分也被分爲了兩個模塊,RIL模塊與Phone模塊。其中RIL模塊負責進行請求以及相應的處理,它將直接與RIL的原聲代碼進行通訊。而Phone模塊則嚮應用程序開發者提供了一系列的電話功能接口。

4.1.RIL模塊結構

在RIL.java中實現了幾個類來進行與下層rild的通訊。

它實現了以下幾個類來完成操做:

RILRequest:表明一個命令請求

RIL.RILSender:負責AT指令的發送

RIL.RILReceiver:用於處理主動和普通上報信息

RIL.RILSender與RIL.RILReceiver是兩個線程。

RILRequest提供了obtain()方法,用於獲得具體的request操做,這些操做被定義在RILConstants.java中(RILConstants.java中定義的request命令與RIL原生代碼中ril.h中定義的request命令是相同的),而後經過send()函數發送EVENT_SEND,在RIL_Sender線程中處理這個EVENT_SEND將命令寫入到stream(socket)中去。Socket是來自常量SOCKET_NAME_RIL,它與RIL 原生代碼部分的s_fdListen所指的socket是同一個。

當有上報信息來到時,系統將經過RILReciver來獲得信息,並進行處理。在RILReciver的生命週期裏,它一直監視着SOCKET_NAME_RIL這個socket,當有數據到來時,它將經過readRilMessage()方法讀取到一個完整的響應,而後經過processResponse來進行處理。

4.2.Phone模塊結構

Android經過暴露Phone模塊來供上層應用程序用戶使用電話功能相關的接口。它爲用戶提供了諸如電話呼叫,短信息,SIM卡管理之類的接口調用。它的核心部分是類GSMPhone,這個是Gsm的電話實現,須要經過PhoneFactory獲取這個GSMPhone。

GSMPhone並非直接提供接口給上層用戶使用,而是經過另一個管理類TelephonyManager來供應用程序用戶使用。

類TelephonyManager實現了android的電話相關操做。它主要使用兩個服務來訪問telephony功能:

1.ITelephony,提供給上層應用程序用戶與telephony進行操做,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java實現。

2.ItelephonyRegistry提供了一個通知機制,將底層來的上報通知給框架中須要獲得通知的部分,由TelephonyRegistry.java實現。

GSMPhone經過PhoneNotifier的實現者DefaultPhoneNotifier將具體的事件轉化爲函數調用,通知到TelephonyRegistry。TelephonyRegistry再經過兩種方式通知給用戶,其一是廣播事件,另一種是經過服務用戶在TelephonyRegistry中註冊的IphoneStateListener

相關文章
相關標籤/搜索