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();
}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值