RILD - 第三章 - ReaderLooper的建立

三、 ReaderLooper()

ReaderLooper主要用来监听Modem上发信息

RIL需要加载一个AT相关的reference-ril.so的动态链接库。之所以使用 库的形式,就是考虑到每个厂商使用的Modem不同,我们没法用统一的接口去向底层负责,因此使用库的形式。这样一来,不同的Modem厂商提供不同的链接库,只要符合RIL层的框架即可。

3.1 ReadLoop预备知识

3.1.1 ReferenceRIL初始化入口

前面在RILC层函数入口提到RILD会加载reference库来初始化ReferenceRIL,如下:

rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))
      dlsym(dlHandle, "RIL_Init");
funcs = rilInit(&s_rilEnv, argc, rilArgv);

我们来看看:
第一行为从库为加载RIL_Init函数。
第二行有很值得分析的地方,主要有三点:
1. 执行rilInit()函数(即RIL_Init)创建ReadLoop,这点在后面会详细讨论。
2. s_rilEnv,这是一个包含LibRIL回调函数的结构体,将其传入ReferenceRL后,使得ReferenceRIL能够调用LibRIL的函数向其通信。
3. funcs,这是一个包含ReferenceRIL回调函数的结构体,其从ReferenceRIL返回给LibRIL,使得LibRIL能够调用ReferenceRIL的函数向其通信。

如上2,3点使得LibRIL和ReferenceRIL都拥有对方的回调,从而能够是他们之间进行双向通信。
下面来看看这两个包含回调函数的结构体。

3.1.2 LibRIL回调(RIL_Env)

其结构体定义为:

//@mtk_ril.h
struct RIL_Env {
    /**
     * "t" is parameter passed in on previous call to RIL_Notification
     * routine.
     *
     * If "e" != SUCCESS, then response can be null/is ignored
     *
     * "response" is owned by caller, and should not be modified or
     * freed by callee
     *
     * RIL_onRequestComplete will return as soon as possible
     */
    void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,
                           void *response, size_t responselen);

#if defined(ANDROID_MULTI_SIM)
    /**
     * "unsolResponse" is one of RIL_UNSOL_RESPONSE_*
     * "data" is pointer to data defined for that RIL_UNSOL_RESPONSE_*
     *
     * "data" is owned by caller, and should not be modified or freed by callee
     */
    void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id);
#else
    /**
     * "unsolResponse" is one of RIL_UNSOL_RESPONSE_*
     * "data" is pointer to data defined for that RIL_UNSOL_RESPONSE_*
     *
     * "data" is owned by caller, and should not be modified or freed by callee
     */
    void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen);
#endif
    /**
     * Call user-specifed "callback" function on on the same thread that
     * RIL_RequestFunc is called. If "relativeTime" is specified, then it specifies
     * a relative time value at which the callback is invoked. If relativeTime is
     * NULL or points to a 0-filled structure, the callback will be invoked as
     * soon as possible
     */

    void (*RequestTimedCallback) (RIL_TimedCallback callback,
                                   void *param, const struct timeval *relativeTime);
    /**
     * "t" is parameter passed in on previous call RIL_Notification routine
     *
     * RIL_onRequestAck will be called by vendor when an Async RIL request was received
     * by them and an ack needs to be sent back to java ril.
     */
     void (*OnRequestAck) (RIL_Token t);

#if defined(MTK_RIL) || defined(C2K_RIL)
    /**
     * Same propose as RequestTimedCallback but executed in proxy thread
     */
    void (*RequestProxyTimedCallback) (RIL_TimedCallback callback, void *param,
                        const struct timeval *relativeTime, int proxyId);

    /**
     * Query Context from RIL_Token
     */
    RILChannelId (*QueryMyChannelId) (RIL_Token t);

    /**
     * Query current proxy according to thread
     */
    int (*QueryMyProxyIdByThread)();
#endif
}; 

结构体变量在rild.cpp中定义,其函数在ril.cpp中实现

//@rild.cpp
static struct RIL_Env s_rilEnv = {
    RIL_onRequestComplete, //请求完成,向上反馈结果
    RIL_onUnsolicitedResponse, //UNSOL信息反馈
    RIL_requestTimedCallback,
    RIL_onRequestAck
#ifdef MTK_RIL
    ,RIL_requestProxyTimedCallback
    ,RIL_queryMyChannelId
    ,RIL_queryMyProxyIdByThread
#endif
};
3.1.3 ReferenceRIL回调(RIL_RadioFunctions)

其结构体定义为:

//@mtk_ril.h
typedef struct {
int version;        //当前链接库的版本信息
RIL_RequestFunc onRequest; //用于Event侧向动态库发起请求
RIL_RadioStateRequest onStateRequest; //得到当前库的状态
    RIL_Supports supports;   //查询是否支持某个命令
    RIL_Cancel onCancel;    //取消一个Event的处理
    RIL_GetVersion getVersion; //得到版本号
#ifdef C2K_RIL
    RIL_ReportSocketConn reportRILDConn;
#endif
} RIL_RadioFunctions;

其中除了version之外,如RIL_RequestFunc都是函数指针类型。
这些函数的实现与结构体变量定义在ril_callbacks.c:

// ril_callbacks.c
static const RIL_RadioFunctions s_callbacks = {
    RIL_VERSION,
    onRequest,
    currentState,
    onSupports,
    onCancel,
    getVersion
};

3.2 RIL_Init执行流程

创建线程执行mainloop函数

const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv){
    s_rilenv = env;
    ...
    //异常判断
    ...
    //创建线程执行mainloop函数
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);

    //返回回调函数
    return &s_callbacks;
}
3.2.1 mainloop()

mainloop中循环打开AT通道获取数据

static void *mainLoop(void *param){
    //readloop关闭时回调函数
    at_set_on_reader_closed(onATReaderClosed);
    //readloop超时时回调函数
    at_set_on_timeout(onATTimeout);
    //初始化channel
    initRILChannels();

    fd = -1;
    while (fd < 0) {
        //与AT通道有关的Context信息,包括FD标识符
        RILChannelCtx * p_channel;
        ...
        for (i=0; i < getSupportChannels(); i ++){
            //可以有多个at通道,(针对多卡的情况?)
            p_channel = getChannelCtxbyId(i);
            //打开fd对应的AT通道,并将UNSOL处理方法onUnsolicited传入
            ret = at_open(p_channel->fd,onUnsolicited, p_channel); 
        }
        //阻塞线程
        /*
        AT通道在检测超时后,将会主动的关闭当前的AT通道,此时将会激活waitForClose中的阻塞线程
        然后waitForClose将会返回
        而一旦waitForClose函数返回,将会再次进入for循环,重新打开AT通道。
        */
        waitForClose();
    }
}
3.2.2 at_open()

打开AT通道,就是创建线程去读fd内容,读到的内容一般交给processline来处理。
processline区分UNSOL和SOL消息分别进行处理,这里注意判断UNSOL和SOL消息的依据,一般都是通过prefix来判断。

int at_open(int fd, ATUnsolHandler h)
{
    ...
    //at通道文件句柄,这里赋给全局变量
    s_fd = fd;
    //UNSOL信息处理函数,这里赋给全局变量
    s_unsolHandler = h;
    ...
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);
    ...
    return 0;
}

static void *readerLoop(void *arg)
{
    for (;;) {
        const char * line;
        //从AT通道文件句fd中中读一行,如果超时,返回NULL
        line = readline();

        //如果没读到东西,break
        if (line == NULL) {
            break;
        }

        if(isSMSUnsolicited(line)) {
            //对于SMS UNSOL消息的处理,注意这个s_unsolHandler是在at_open中传入的函数参数。
            s_unsolHandler (line1, line2);
            free(line1);
        } else {
            //大部分消息在这里处理
            processLine(line);
        }
    }
    //读AT通道结束时的回调
    onReaderClosed();

    return NULL;
}

static void processLine(const char *line)
{

    //sp_response为NULL,表示为USOL消息
    //sp_response下面再讨论
    if (sp_response == NULL) {
        /* no command pending */
        handleUnsolicited(line);
    } else if (isFinalResponseSuccess(line)) {
        //对于SOL消息的处理
        sp_response->success = 1;
        handleFinalResponse(line);
    }
    ...
    pthread_mutex_unlock(&s_commandmutex);
}

上面的readerLoop在对Modem返回数据进行处理是分两条线:
1. 短信相关UNSOL处理
2. 普通UNSOL处理

Android手机接收到Modem发出有线短信的AT命令,其文本格式非常固定,共有两行line1和line2,因此它可以直接在第一个分支做特殊处理;而调用processLine()函数处理普通AT命令,其中包括了多种处理方式,最典型的如SINGLELINE,MULTILINE和NO_RESULT等,虽然处理方式不同,但他们最终会调用handleUnsolicited。如下分析。

3.2.3 UNSOL消息处理

对不同类型的UNSOL进行分类处理

static void handleUnsolicited(const char *line)
{
    if (s_unsolHandler != NULL) {
        //s_unsolHandler来自at_open的参数,即为onUnsolicited函数,其实现在ril_callbacks.c中
        s_unsolHandler(line, NULL);
    }
}

//@ril_callbacks.c
static void onUnsolicited(const char *s, const char *sms_pdu, void *pChannel)
{
    char *line = NULL;
    int urcToMalLength = 0;
    int err;
    RIL_RadioState radioState = sState;
    RILChannelCtx *p_channel = (RILChannelCtx *)pChannel;

    //从RILChannelCtx获取Radio状态
    if (RIL_SOCKET_2 == getRILIdByChannelCtx(p_channel)) {
        radioState = sState2;
    } else if (RIL_SOCKET_3 == getRILIdByChannelCtx(p_channel)) {
        radioState = sState3;
    } else if (RIL_SOCKET_4 == getRILIdByChannelCtx(p_channel)) {
        radioState = sState4;
    }

    //如果RIL状态不可用,,返回
    ...

    //不同类型的UNSOL信息的处理,这些函数主要是通过s信息的皮prefix来判定不同的消息类型
    if (!(rilNwUnsolicited(s, sms_pdu, p_channel) ||
          rilCcUnsolicited(s, sms_pdu, p_channel) ||
          rilSsUnsolicited(s, sms_pdu, p_channel) ||
          rilSmsUnsolicited(s, sms_pdu, p_channel) ||
          rilStkUnsolicited(s, sms_pdu, p_channel) ||
          rilOemUnsolicited(s, sms_pdu, p_channel) ||
          rilDataUnsolicited(s, sms_pdu, p_channel) ||
          rilSimUnsolicited(s, sms_pdu, p_channel) ||
          rilPhbUnsolicited(s, sms_pdu, p_channel)))
        RLOGE("Unhandled unsolicited result code: %s\n", s);
}

针对上面的每种分类的UNSOL,都有对应的文件定义其处理方式,以便于解耦
上面以NetWork类型的UNSOL为例,其处理函数定义在mtk_ril/ril_nw.c中。
最终这些经过解析的UNSOL信息都会调用RIL_UNSOL_RESPONSE
RIL_UNSOL_RESPONSE通过宏定义到LibRIL的RIL_onUnsolicitedResponse((a), (b), (c))
因此这些UNSOL信息最终传递到了LibRIL中

//@ril_nw.c
int rilNwUnsolicited(const char *s, const char *sms_pdu, RILChannelCtx* p_channel)
{
    RIL_SOCKET_ID rid = getRILIdByChannelCtx(p_channel);

    //通过prefix判断是否为网络状态信息的UNSOL
    if (strStartsWith(s, "+CREG:") || strStartsWith(s, "+CGREG:") || strStartsWith(s, "+PSBEARER:") || strStartsWith(s, "+CEREG:"))
    {
        onNetworkStateChanged((char*) s,rid);
        return 1;
    }
    ...

}

//@ril_nw.c
void onNetworkStateChanged(char *urc, const RIL_SOCKET_ID rid){
    //解析UNSOL信息参数urc,构建网上传递的信息

    //针对Voice NewtWork状态和PS NewtWork状态,调用LibRIL回调函数往上传
    if (is_cs == 1){
        RIL_UNSOL_RESPONSE (RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED,
responseStr,  sizeof(responseStr), rid);
    }else{
        RIL_UNSOL_RESPONSE(RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED,
&stat, sizeof(stat), rid);
    }
}

最后在LibRIL中,将数据进行打包,发送给RILJ

void RIL_onUnsolicitedResponse(int unsolResponse, const void *data,
    size_t datalen, RIL_SOCKET_ID socket_id){

    unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE;

    //通过unsolResponse获取对应的返回类型
    if (unsolResponse >= RIL_LOCAL_GSM_UNSOL_VENDOR_BASE) {
        unsolResponseIndex = unsolResponse - RIL_LOCAL_GSM_UNSOL_VENDOR_BASE;
        wakeType = s_mtk_local_urc_commands[unsolResponseIndex].wakeType;
    }
    else if (unsolResponse >= RIL_UNSOL_VENDOR_BASE) {
        unsolResponseIndex = unsolResponse - RIL_UNSOL_VENDOR_BASE;
        //s_mtk_unsolResponses是UnsolResponseInfo类型的数组
        wakeType = s_mtk_unsolResponses[unsolResponseIndex].wakeType;
    }
    else
    {
        wakeType = s_unsolResponses[unsolResponseIndex].wakeType;
    }
    ...
    //正对不同类型进行处理
    switch (wakeType) {
        case WAKE_PARTIAL:
            grabPartialWakeLock();
            shouldScheduleTimeout = true;
        break;

        case DONT_WAKE:
        default:
            // No wake lock is grabed so don't set timeout
            shouldScheduleTimeout = false;
            break;
    }
    ...
    Parcel p;
    ...
    if (unsolResponse >= RIL_LOCAL_GSM_UNSOL_VENDOR_BASE) {
        //调用对应UnsolResponseInfo的处理函数进行处理,将数据写入Parcel
        ret = s_mtk_local_urc_commands[unsolResponseIndex]
                .responseFunction(p, const_cast<void*>(data), datalen);
    }
    else if (unsolResponse >= RIL_UNSOL_VENDOR_BASE) {
        ret = s_mtk_unsolResponses[unsolResponseIndex]
              .responseFunction(p, const_cast<void*>(data), datalen);
    } else
    {
        ret = s_unsolResponses[unsolResponseIndex]
              .responseFunction(p, const_cast<void*>(data), datalen);
    }
    ...
    //针对不同标识的UNSOL进行Parcel加工处理
    ...
    //发送到RILJ中
    ret = sendResponse(p, soc_id);
}

static int
sendResponse (Parcel &p, RIL_SOCKET_ID socket_id) {
    printResponse;
    return sendResponseRaw(p.data(), p.dataSize(), socket_id);
}

static int
sendResponseRaw (const void *data, size_t dataSize, RIL_SOCKET_ID socket_id) {
    //从Socket连接参数的数组中获取Socket的FD
    int fd = s_ril_param_socket[socket_id].fdCommand;
...
//将数据长度这个整数转换为适合Socket  传输的字节顺序
header = htonl(dataSize);
//先发送数据大小
ret = blockingWrite(fd, (void *)&header, sizeof(header));
//再发送数据,将信息写入Socket传到RILJ
//这个fd是在RIL_register—>startListenadd listen Event,在EventLoop中监听获取
    ret = blockingWrite(fd, data, dataSize);
}
3.2.4 SOL消息处理

针对SOL消息的处理,相对于UNSOL稍稍复杂一点.
在3.3.2中,可以看到,最终是使用了handleFinalResponse()函数来进行SOL消息的处理:

static void processLine(const char *line, RILChannelCtx *p_channel){
    ...
    //将数据封装到p_channel->p_response->p_intermediates中
    addIntermediate(line, p_channel);
    ...
    //设置成功标志位
    p_response->success = 1;
    //处理数据返回
    handleFinalResponse(line, p_channel);

}

static void handleFinalResponse(const char *line, RILChannelCtx *p_channel)
{
    //将数据放入p_channel->p_response->finalResponse
    ATResponse *p_response = p_channel->p_response;
    p_response->finalResponse = strdup(line);
    //唤醒s_commandcond线程,使其脱离阻塞状态
    pthread_cond_signal(&p_channel->commandcond);
}

handleFinalResponse()函数只是把返回的结果放到了finalResponse结构中,为什么会到这里就结束了呢?难道是有其他地方在一直监控这个变量,当它不为NULL时,就表示了返回结果?答案时肯定的。

对于一条SOL消息,必定对应了一条request,在request向Modem发送了请求会后会在循环中检测p_channel->p_response的状态,在对应SOL消息未返回之前,一直为NULL,当handleFinalResponse函数将返回值写入p_response之后,就会跳出循环开始读取p_response数据。

详见的执行流程可以参考atchannel.c的at_send_command_full_nolock()函数。当然后面的的整体流程分析中也会再拿出来说明。

3.3 总结

ReaderLooper主要的功能就是读取并处理Modem上发的消息,这些消息总体分为UNSOL和SOL消息,然后针对这两种类型的消息,分别做不同的处理,最终的结果都是会通过LibRIL上发给RILJ。

你可能感兴趣的:(Android,-,Telephony)