Rild框架及流程

前言

個人學習過程總結

相關參考資料:
深入剖析 Android 系統_楊長剛/第 9 章 RIL
安卓 4.1 MTK 源碼

整體框架

在這裏插入圖片描述

Rild 框架

RIL(Radio Interface Layer) 是上層程序使用地射頻功能的接口層,它更像一個支持數據格式轉換的

通道。
上層程序的 API 調用最終轉換爲底層射頻能識別出的命令字符串,底層上報的字符串信息被翻譯解釋
後,又能被上層的程序識別,這就是 RIL 層的功能。

RIL 層工作流程:

        Phone 進程        【Java】           
          RILJ 
    ----- socket -------
           Rild           【C/C++】
    ----- serial ------- AT 命令
           Modem           【硬件】

主要進程關係:

在這裏插入圖片描述
【rild 進程】
1. 創建 eventLoop 線程
【eventLoop 線程】: 套接字監聽執行回調,執行提交來的定時任務函數
2. 創建 mainLoop 的工作線程
/////////////////////////////////////////////////////////////////////////
// 以下兩個線程都是 reference-ril.c 硬件動態庫中創建的
【mainLoop 線程】:打開 AT 串口,創建一個定時任務給 eventLoop 初始化 Modem
創建工作線程 readerLoop:
【readerLoop 線程】:主要任務就是從 AT 的串口設備讀取數據,並解析處理

相關文件關係:

RILConstants.java           // 保存了 Java 層的請求號等信息,要求與【硬件庫的 ril.h 文件使用的請求號保持一致】


ril_commands.h              // 定義了請求號與對應分發命令與回覆命令的對應關係,舉例:
                            //      {RIL_REQUEST_DIAL, dispatchDial, responseVoid},
                            // 請求號 10 對應的分發函數爲 dispatchDial, 回覆函數爲 responseVoid
                            //  dispatchDial()/responseVoid() 實現在 ril.cpp 中
                            // 
                            // 【RIL_REQUEST_DIAL】: 定義在 Ril.h 中,被 Java 層與 Reference-ril.c 共用,以便請求保存一致
                            // 【dispatchDial()】: 調用 reference-ril.c 導出的 onRequest() 函數請求 Modem 服務 
                            // 【responseVoid()】: 用於在命令成功返回時,將 AT 回覆的數據寫入 Parcel 

ril_unsol_commands.h        // 定義了 unsolicited Response 的請求號與命令對應關係,舉例:
                            //      {RIL_UNSOL_CALL_RING, responseCallRing, WAKE_PARTIAL},
                            // 【RIL_UNSOL_CALL_RING】:請求號,同樣定義在 ril.h 中 
                            // 【responseCallRing】:定義在 ril.cpp 中,將 AT 回覆的數據寫入 Parcel 
                            // 【WAKE_PARTIAL】:回覆時獲得一個 wake lokc 休眠鎖

                            /////////////////////////////////////////////////////////////////////////
                            // 上面兩個頭文件都定義在 ril.cpp 中引用 
                            //    【Index == requestNumber】 
                            // static CommandInfo s_commands[] = {
                            // #include "ril_commands.h"
                            // };
                            //                             
                            // static UnsolResponseInfo s_unsolResponses[] = {
                            // #include "ril_unsol_commands.h"
                            // };
                            /////////////////////////////////////////////////////////////////////////

ril.cpp                     // rild 中調用相關函數實現,如 RIL_startEventLoop(), RIL_register 等等函數,是 rild 核心實現
        // 這裏定義了會被 reference-ril 廠家庫調用的回調函數
        // static struct RIL_Env s_rilEnv = 
        // {
        //     RIL_onRequestComplete,          // 動態庫完成一個請求後,通過這個函數通知處理結構,其中第一個參數標明是哪個請求的處理結果
        //     RIL_onUnsolicitedResponse,      // 動態庫用於進行 unsolicited Response 通知的函數: 即 BP 主動上報的事件的處理
        //     RIL_requestTimedCallback        // 向 Rild 提交一個超時任務, 即在 eventloop 指定時間後執行的函數
        // };

reference-ril.c             // modem 相關硬件操作的庫函數,用於與 modem 通信 
        // 動態庫開放出來的外部操作函數接口,供外部函數請求調用
        // static const RIL_RadioFunctions s_callbacks = {
        //     RIL_VERSION,
        //     onRequest,
        //     currentState,
        //     onSupports,
        //     onCancel,
        //     getVersion
        // };
rild.c                      // 是 rild 模塊的相關邏輯程序,引用上面兩個庫函數,控制先做什麼,後做什麼

Rild 與上層握手過程:

rild 監聽套接字 /dev/socket/rild 等待連接
    -> 上層發起連接 
        -> eventloop 進程
            -> listenCallback()
                -> 檢查連接權限 
                    1. 正確
                        新套接字 = accept()
                        創建一個 processWakeupCallback() 函數處理新套接字請求
                    2. 不正確
                        重新提交 listenCallback() 函數監聽 /dev/socket/rild 

Rild 與上層通信數據流程:

上層通過套接字發命令 
    ----------- eventloop 線程 ----------------------------------------
    -> eventloop 線程
        -> processCommandsCallback()
            -> 調用 ril_commands.h 定義的分發函數發送命令給 AT 
            -> 等待 AT 設備響應 
            ------------ reader 線程 ----------------------------------
                reader 線程
                    -> AT 串口有數據發送上來 
                        -> processLine
                        喚醒 eventloop 線程
            ----------- eventloop 線程 -------------------------------------
            -> eventloop 繼續執行 
            -> sendResponse
                -> 調用 ril_commands.h 定義的回覆函數封裝一些消息到 Parcel 對象中 
                -> 通過套接字將執行結果返回給上層

上層發來的命令格式:

////////////////////////////////////////////////////////////////
//  【上層發來的請求的格式】
// typedef struct RequestInfo {
//     int32_t token;                        //this is not RIL_Token
//     CommandInfo *pCI;
//              typedef struct {
//                  int requestNumber;       // 請求號 
//                  void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
//                  int(*responseFunction) (Parcel &p, void *response, size_t responselen);
//              } CommandInfo;       
//     struct RequestInfo *p_next;
//     char cancelled;
//     char local;         // responses to local commands do not go back to command process
// } RequestInfo;

Rild 初始化流程總結:

Rild.c (hardware\ril\rild)

// 1. 讀取 system.prop 中的系統屬性:
//          rild.libpath=/system/lib/libreference-ril.so            // 與 modem 通信的具體廠家實現的動態庫
//          rild.libargs=-d /dev/ttyS0                              // 與 modem 通信使用的串口 
//     通過 dlopen 系統加載廠家實現的動態庫
// 
// 2. 啓動 EventLoop 線程, 在這裏進行事件處理,監聽管道套接字,收到數據後調用回調處理
//    同時也可以註冊定時函數讓他到期時調用回調處理
//    -------------------
//    eventLoop 線程 
//         初始化 readFds, 看來 Ril 會使用 select 來做多路 IO 複用
//         創建匿名管道 【用於被喚醒使用】
//         進入事件等待循環中,等待外界觸發事件並做出對應的處理
//             1. 定時任務,由 ril_timer_add() 函數添加,到期執行
//             2. 非定時任務,這些任務的 FD 加入到 select 中監控,有數據可讀時喚醒執行
//             3. 遍歷 pending_list,執行任務函數
// 
// 3. 得到 RefRil 庫中的 RIL_Init 函數的地址 
//  【注意】此函數是在硬件動態庫中實現的,裏面所有函數都是動態庫內部的,除了傳入的接口
//   調用 RefRil 庫輸出的 RIL_Init 函數,注意此函數傳輸的第一個參數和它的返回值 
//     參考動態庫的源碼位於: Reference-ril.c 
//     此函數主要完成工作爲:
//          1. 創建一個 mainLoop 工作線程,它的任務是初始化 AT 模塊,並監控 AT 模塊,一旦
//              AT 模塊被關閉,則會重新初始化 AT 模塊 
//          2. AT 模塊內部會創建一個工作線程 readerLoop,該線程的作用是從串口設備中讀取信息
//              ,也就是直接和 BP 打交道 
//          3. mainLoop 通過向 Rild 提交超時任務,完成了對 Modem 的初始化工作
// 
// 
// 4. 註冊上面 rilInit 函數的返回值(一個 RIL_RadioFunctions 類型的結構體)到 Rild 中,用於 AP 發送命令給 BP 
//     // RIL_register() 將創建兩個監聽端 socket:
//     //      1. rild :       用於與 Java 層的應用通信 
//     //      2. rild-debug:  用來接收測試程序的命令
//     RIL_register(funcs);
// 
// 5. 主線程  sleep, 具體工作交給工作線程完成
//     while(1) {
//         // sleep(UINT32_MAX) seems to return immediately on bionic
//         sleep(0x00ffffff);
//     }

Rilj 框架

在這裏插入圖片描述

與 rild 守護進程交互的 Java 部分是 com.android.internal.telephony.RIL 類(簡稱爲 RILJ),
它是通過 UNIX 套接字(/dev/socket/rild)進行通信。進程 com.android.phone 通過使用 Teleohony
框架來使用系統的各種 Teleohony 功能。

具體框架類似:

        ------ phone 進程 --------    【Java】
        android         Telephony
        framework
            -------------
                RILJ  
        --------socket ----------
                  /\  
                  ||
                  \/
        ------- rild 模塊 -----       【C/C++】
        --------- AT 串口 -------
                硬件設備

RILJ 中各類功能:

//    RILJ 負責與 rild 守護進程交互,提供 API 用於執行 RIL 請求,供 Telephony Framework 調用 
// 它還提供底層對 RIL 請求的響應回覆的處理,它將以消息的形式發送給調用者。
RILJ {

    RILSender: 是 Handler 的子類,將在發送線程中處理髮送消息等事件 

    RILReceiver: 實現了 Runnable 接口類的 Run 接口函數,作爲線程的執行體運行在 RILJ 創建 
                    接收線程(見 RIL 類的構造函數)中,負責消息的接收
}

相關類介紹:

RILConstants:定義了 RIL 的各種請求號和 unsolicited 號,它們必須與 ril.h 中定義保持一致,
                    用於標識請求號和 unsolicited 消息號,這些標誌用在硬件動態庫與 rild 中使用。
CommandsInterface: 接口類,定義了 RILJ 和 rild 交互的接口

BaseCommands implements CommandsInterface: 實現了 CommandsInterface 的部分接口,用於通知手機各種內部
                    狀態的變化,它裏面包含了很多註冊者 Registrant 和註冊者列表 RegistrantList, 它們
                    代表着對某些事件感興趣希望接收事件變化通知的接收者。
                        當對某種狀態變化感興趣時,就可以調用 registerXXX 函數將自己註冊爲一個對某種
                    狀態感 興趣的通知接收者。
                        註冊時,在 BaseCommands 內部創建一個對應的 registrant 將 registrant 添加到
                    列表 registrantList 中。
                        調用 registerXXX() 時,調用者將 message 和 handler 傳遞給 Registrant 對象,
                    當有狀態變化(processUnsolicited 處理主動上報的信息則往往意味着狀態的改變)時,則
                    通過 Registrant.NotifyXXX() 調用 hander 將消息發到消息隊列上,Handler 所在線程將
                    處理這些消息,這也保證了儘可能的實時通知調用者的目的。

RILRequest: 代表着一個即將發送出去的 RIL 請求,它裏面包含了 Request 請求號、序列號(自 0 開始累加)和保存
                    請求結果的 Message,Java 部分的 Request 請求號就是上述的 RILConstants 中的常量(與 C/C++
                    部分的請求號保持一致)。當需要執行某種 RIL 請求時,則需創建一個新的 RILRequest 使用 
                    RILRequest 的 obtain 函數  

RILJ 類的初始化:

///////////////////////////////////////////////////////////////////////////////////////////
// 1. 外部創建一個對象,使用時應該是直接將此類當作一個處理線程運行,用來與 rild 通信發命令
    // 是一個內部類
    RIL::RILSender extends Handler implements Runnable  // 繼承 Runnable 多線程類,
                                                        //  Handler: 給消息隊列發消息並調用回調處理
    {
        // 構造函數傳入的參數是一個消息隊列
        // 表示與一個消息隊列綁定,給此消息隊列發消息,
        // 當目標進程處理消息隊列中此條消息時,會調用發送時傳入的回調處理,即本類的 handleMessage() 處理 
        RILSender(Looper looper)
        {
            super(looper);
        }

        // 外部調用 start 時將此函數當線程啓動
        run(){}

        // 當在構造函數中綁定的消息隊列有消息時,調用此函數進行解析處理
        handleMessage(Message msg) 
        {

        }
    }

//////////////////////////////////////////////////////////////////////////////////////////
// 2. 創建一個接收,接收 rild 回覆的消息
    // 內部類
    RIL::RILReceiver implements Runnable // 繼承 Runnable ,多線程類
    {
        // 外部 start 時將此函數當線程啓動
        run() {

            for (;;) {
                // 連接上 rild 內部的套接字 /dev/socket/rild 
                s = new LocalSocket();
                l = new LocalSocketAddress(socketRil, LocalSocketAddress.Namespace.RESERVED);
                s.connect(l);
                
                InputStream is = mSocket.getInputStream();
                for (;;) {
                    // 從套接字讀數據
                    length = readRilMessage(is, buffer);
                    // 沒數據退出循環
                    if (length < 0) {
                        // End-of-stream reached
                        break;
                    }
                    
                    // 獲得套接字傳入的數據
                    p = Parcel.obtain();
                    p.unmarshall(buffer, 0, length);
                    p.setDataPosition(0);

                    ///////////////////////////////////////
                    // 進行數據處理 
                    processResponse(p);
                }
                
                // 從套接讀數據返回卻沒讀到數據?復位 AT 串口
                setRadioState (RadioState.RADIO_UNAVAILABLE);
                mSocket.close();
                RILRequest.resetSerial(mySimId);

            }
        }

    }

【發送消息的流程:】

//////////////////////////////////////////////////////////////////////////////////////////
// 調用相應的 RIL 請求對應的成員函數發給命令,比如: 向 SIM/USIM 卡提供 PIN 碼對應的函數 
// 所有的接口位於 CommandsInterface 接口類中定義中:

    RIL::supplyIccPinForApp(String pin, String aid, Message result) 
    {
        //Note: This RIL request has not been renamed to ICC,
        //       but this request is also valid for SIM and RUIM
        ///////////////////////////////////////////////////////////////////////////////////
        // 1. 獲得一個請求對象,他保存在一個緩衝池中,當命令回覆時需要釋放
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN, result, mySimId);
                                // 獲得一個 RILRequest 對象 
                                RILRequest::obtain()
                                    // 從內部維護的 RIL_request 池 sPool中取下一個 request, 得到一個 RILRequest 實例
                                    // 它裏面的請求號和請求結構消息來自傳遞的實參, 傳遞的參數:
                                    //      1. RIL 請求號 
                                    //      2. 請求結果保存位置
                                    //      3. 返回結果的處理者(Message 中的 Handler),【當有結果時通過此給目標消息隊列發消息】
                                    if (sPool != null) {
                                        rr = sPool;
                                        sPool = rr.mNext;
                                        rr.mNext = null;
                                        sPoolSize--;
                                    }

                                    if (rr == null) {
                                        rr = new RILRequest();
                                    }

        //////////////////////////////////////////////////////////////////////
        // 待寫入 socket 中的字符串個數 
        rr.mParcel.writeInt(2);
        // 寫入 pin 碼字符串
        rr.mParcel.writeString(pin);
        // 寫入其他附屬信息
        rr.mParcel.writeString(aid);

        ////////////////////////////////////////////////////////////////////
        // 發送請求,由 sender 線程完成向 socket 寫入數據 
        send(rr) 
            send(RILRequest rr) {
                Message msg;
                if (mSocket == null) {
                    rr.onError(RADIO_NOT_AVAILABLE, null);
                    rr.release();
                    return;
                }
                // 獲得一個消息對象,
                //      消息類型爲:EVENT_SEND, 
                //      消息數據爲: 要寫入套接字發給底層 rild 的數據
                msg = mSender.obtainMessage(EVENT_SEND, rr);

                // 獲得休眠鎖?
                acquireWakeLock();

                //////////////////////////////////////////////////////////////////////////////
                // 將消息添加到目標消息隊列中,如果有需要,喚醒目標消息隊列所在線程
                msg.sendToTarget();
                    Messgae::sendToTarget()
                        target.sendMessage(this);
                            //////////////////////////////
                            // 調用 Handler 類發送消息
                            Handler::sendMessage()
                                sendMessageDelayed(msg, 0);
                                    sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
                                        enqueueMessage(queue, msg, uptimeMillis);
                                            queue.enqueueMessage(msg, uptimeMillis);
                                                /////////////////////////////////////
                                                // 調用 MessageQueue 類發送消息 
                                                MessageQueue::enqueueMessage()
                                                    // 這裏僅僅當消息插入目標消息隊列,然後判斷是否需要喚醒目標線程
                                                    // 如果需要,則調用  nativeWake() 喚醒目標線程,【流程見情景分析】
                                                    //【注意】目標消息隊列是初始化 Hander 對象時指定的。

            }
    }
    

    //////////////////////////////////////////////////////////////////
    // RIL 初始化時構造的 RILSender 線程接收到此上面要發送的數據的消息
    // 調用其 handlerMessage() 函數處理消息 
    RILSender::handleMessage(Message msg)
    {
        // 獲得發來的消息類型
        RILRequest rr = (RILRequest)(msg.obj);
        // 根據不同的消息類型處理
        switch (msg.what) {
            ///////////////////////////////
            // 通過套接字發送命令給 rild 
            case EVENT_SEND:
                    // parcel 中是待寫入數據的緩衝區,經過 marshall 賦給 data 
                    data = rr.mParcel.marshall();
                    // 往 socket 寫入數據長度
                    s.getOutputStream().write(dataLength); 
                    ////////////////////////////////////////
                    // 寫入數據給 rild 【底層 rild 怎麼接收處理數據,見 rild 分析】
                    s.getOutputStream().write(data);

        }

    }

【接收消息處理流程:】

以 RILReceiver 初始化已經有介紹怎麼收到數據,這裏只介紹消息的處理:
// RILReceiver 接收到消息後,最終調用 processResponse() 函數處理  
RIL::processResponse(Parcel p)
{
    ////////////////////////////////////////
    // 1. 處理主動上報的消息
    if (type == RESPONSE_UNSOLICITED)
        processUnsolicited (p);
                // 讀取回復請求號,位於 ril_unsol_commands.h 中的 
                // 獲得所有回覆請求號, 應該與 ril_unsol_commands.h 文件中保持一致?
                //    cat libs/telephony/ril_unsol_commands.h \
                //    | egrep "^ *{RIL_" \
                //    | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: \2(rr, p); break;/'
                //
                response = p.readInt();
                
                //////////////////////////////////////////////////////////////
                // 1. 根據不同的回覆請求,來調用不同處理函數, 以獲得上報的數據
                switch(response){
                    case RIL_UNSOL_ON_USSD: ret =  responseStrings(p); break;
                                                        // 僅僅是從回覆的數據中提取出要返回的字符串
                                                        responseStrings(Parcel p) {
                                                                int num;
                                                                String response[];

                                                                response = p.readStringArray();

                                                                return response;
                                                            }
                    。。。
                }
                //////////////////////////////////////////////////////////////
                // 2. 對相應結果作進一步處理,如通知感興趣的註冊者 
                switch(response){
                    case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
                            // 從回覆的消息中獲得狀態
                            RadioState newState = getRadioStateFromInt(p.readInt());
                            // 更新狀態
                            switchToRadioState(newState);
                }

    /////////////////////////////////////////
    // 2. 處理 RIL 請求的執行回覆結果
    else if (type == RESPONSE_SOLICITED) 
        processSolicited (p);
                // 讀取序列號
                serial = p.readInt();
                // 讀取執行成功與否標誌
                error = p.readInt();    

                // 在請求列表查找並摘下 RILRequest 對應項,表示命令發送完成
                // 【這表明在發送時分配的 RILRequest 對就項應該會添加此鏈表中管理,but 沒找到...】
                rr = findAndRemoveRequestFromList(serial);
                                    for (int i = 0, s = mRequestList.size() ; i < s ; i++) 
                                        RILRequest rr = mRequestList.get(i);
                                        if (rr.mSerial == serial)
                                            mRequestList.remove(i);

                ///////////////////////////////////////////////////////////////////////////////////
                // 1. 命令發送成功,且有了返回的結果
                if (error == 0 || p.dataAvail() > 0)
                    // 根據回覆的請求號,調用不同的函數處理上傳的數據,得到要返回的數據
                    try{
                        switch (rr.mRequest){
                            case RIL_REQUEST_GET_SIM_STATUS: ret =  responseIccCardStatus(p); break;
                                                                        // 表示要返回一個 IccCardStatus 的對象,此函數就是根據
                                                                        // 上傳來的數據構造 IccCardStatus 對象
                                                                        RIL::responseIccCardStatus(Parcel p)
                                                                                // 創建一個 IccCardStatus 對象 
                                                                                IccCardStatus cardStatus = new IccCardStatus();
                                                                                
                                                                                // 處理上傳數據,填充 IccCardStatus 對應項 
                                                                                cardStatus.setCardState(p.readInt());
                                                                                cardStatus.setUniversalPinState(p.readInt());
                                                                                。。。 
                                                                                // 返回 IccCardStatus 對象 
                                                                                return cardStatus;

                            // 接收到不能識別的命令,拋出異常
                            default:
                                throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
                        }
                    }catch (Throwable tr) {
                    // 捕捉異常
                            // AsyncResult.forMessage() 將三個實參封裝到一個 AsyncResult 實例中
                            // 然後將該實例賦值給 Message.obj 成員 
                            AsyncResult.forMessage(rr.mResult, null, tr);

                            //////////////////////////////////////////////////////////////////////
                            // 調用 Message.sendToTarget() 將消息發送到初始化時調用的 Handler 
                            // 隊列中,由相應的線程去處理,這裏是將結果送給調用者處理
                            // 這個 Message 對象是在調用 RILRequest::obtain() 設置的,見上調用處
                            rr.mResult.sendToTarget();

                            // 釋放 RILRequest 回池中 
                            rr.release(); 
                            return;
                    }

                ////////////////////////////////////////////////////////////////////////
                // 2. 有執行結果,但出錯了,發消息給調用者處理
                if (error != 0) 
                    //////////////////////////////////////////////////////////
                    // 調用 RILRequest 的錯誤處理函數,發送 msg 消息給調用者
                    rr.onError(error, ret);
                            RILRequest::onError(int error, Object ret)
                                    ex = CommandException.fromRilErrno(error);
                                    if (mResult != null)
                                        AsyncResult.forMessage(mResult, ret, ex);
                                        //////////////////////////////////////////////////////////
                                        // mResult 是在 RILRequest::obtain() 時設置的 Message 對應
                                        // 所以是發給調用者處理
                                        mResult.sendToTarget();

                    // 釋放 RILRequest 回池中 
                    rr.release();
                    return;
                
                ///////////////////////////////////////////////////////////////////////////
                // 3. 有執行結果,發消息給調用處理 
                if (rr.mResult != null) {
                    AsyncResult.forMessage(rr.mResult, ret, null);
                    rr.mResult.sendToTarget();
                }
                // 釋放 RILRequest 回池中 
                rr.release();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章