必威体育Betway必威体育官网
当前位置:首页 > IT技术

RIL

时间:2019-09-01 17:12:10来源:IT技术作者:seo实验室小编阅读:56次「手机版」
 

ril

对于熟悉Android O之前RIL的开发者来说,android o上RIL最大的改变就是将socket通信换成了binder通信,只不过是/dev/hwbinder,而不是/dev/binder。Binder IPC的开发模式是服务端注册service, 客户端获取service,然后调用相关API。Server端RILD不再是socket监听,而是注册service; RILJ也不再是连接socket,而是获取service,持有引用。通信方式的改变导致RILD和RILJ都发生了改变,但是这个变化并不算大,以前的轮廓还在。

下面根据Android O 源码,从下面两个方面来简单介绍下RIL:

1.RILD daemon部分

2.RIL java 部分

RIL daemon

先从RIL daemon部分开始,因为这样思路比较顺畅。这部分相关代码路径是”/hardware/ril/rild/”,根据Android.mk文件,这个部分code会编译生成可以执行文件,init进程会根据rild.rc来加载运行该文件。

下面是rild.rc中的内容, “/vendor/bin/hw/rild”是binary手机中的路径:

service ril-daemon /vendor/bin/hw/rild
    class main
    user radio
    group radio cache inet misc audio log readproc wakelock
    capabilities BLOCK_SUSPEND NET_ADMIN NET_RAW

rild被加载执行的时候rild.c的main函数会被执行, 该函数主要完成了下面的工作

1.加载vendor需要的lib, 获取lib的handle

2.获取Ril_Init函数; 调用Ril_Init函数获取RIL_RadioFunctions类型的返回值。RIL_RadioFunctions是一个结构体,成员全是函数指针,可以通过这些函数指针调用lib内的函数。

3.调用RIL_register函数注册service(该函数的参数是Ril_Init返回的函数指针,该函数内部会将函数指针复制给全局变量保存)。

注册service

RIL_register函数的定义在Ril.cpp文件内。这个函数只是做了一些参数检查工作,然后直接调用了Ril_service.cpp内的registerService函数。

void radio::registerService(RIL_RadioFunctions *callbacks, commandInfo *commands) {
    using namespace android::hardware;
    int simCount = 1;
    const char *serviceNames[] = {//根据SIM的数量来确定service的名称。
            android::RIL_getServiceName()
            #if (SIM_COUNT >= 2)
            , RIL2_SERVICE_NAME
            #if (SIM_COUNT >= 3)
            , RIL3_SERVICE_NAME
            #if (SIM_COUNT >= 4)
            , RIL4_SERVICE_NAME
            #endif
            #endif
            #endif
            };

    #if (SIM_COUNT >= 2)
    simCount = SIM_COUNT;
    #endif

    configureRpcThreadpool(1, true /* callerWillJoin */);
    for (int i = 0; i < simCount; i++) {
        pthread_rwlock_t *radiOServiceRwlockPtr = getRadioServiceRwlock(i);
        int ret = pthread_rwlock_wrlock(radioServiceRwlockPtr);
        assert(ret == 0);

        radioService[i] = new RadioImpl;//service对象
        radioService[i]->mSlotId = i;
        oemHookService[i] = new OemHookImpl;
        oemHookService[i]->mSlotId = i;
        RLOGD("registerService: starting IRadio %s", serviceNames[i]);
        android::status_t status = radioService[i]->registerAsService(serviceNames[i]);
        status = oemHookService[i]->registerAsService(serviceNames[i]);//注册service

        ret = pthread_rwlock_unlock(radioServiceRwlockPtr);
        assert(ret == 0);
    }

    s_vendorFunctions = callbacks;//将callbacks存进全局变量, 用于处理request
    s_commands = commands;//
}

第一个参数是Ril_Init函数返回的由函数指针组成的结构体变量; 第二个参数是CommandInfo类型的s_commands变量,该变量是用ril_commands.h初始化的,这个头文件里定义了每个request对应的response函数,下面是ril_commands.h的一部分:

  ......
  {RIL_REQUEST_DIAL, radio::dialResponse},
  {RIL_REQUEST_GET_IMSI, radio::getIMSIForAPPResponse},
  {RIL_REQUEST_HANGUP, radio::hangupConnectionResponse},
  {RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, radio::hangupWaitingOrBackgroundResponse},
  ......

在registerService函数中每个service都是新创建的RadioImpl对象。

struct RadioImpl : public IRadio {
}

RadioImpl是个结构体,继承了IRadio。IRadio也是一个结构体,相关.h和C++文件会在编译”hardware/interfaces/radio/1.0/”路径下的.hal文件时生成(可以参考Android.bp)。编译生成的.h和C++文件在”out/soong/intermediates/hardware/interfaces/”路径下。

我们可以在RadioAll.cpp文件中找到registerAsService的定义:

::android::status_t IRadio::registerAsService(const std::string &serviceName) {
    ::android::hardware::details::onRegistration("[email protected]", "IRadio", serviceName);

    const ::android::sp<::android::hidl::manager::V1_0::IServiceManager> sm
            = ::android::hardware::defaultServiceManager();
    if (sm == nullptr) {
        return ::android::INvalid_OPERATION;
    }
    ::android::hardware::Return<bool> ret = sm->add(serviceName.c_str(), this);
    return ret.isOk() && ret ? ::android::OK : ::android::UNKNOWN_ERROR;
}

到这里知道通信使用的是/dev/hwbinder就可以了。再往下追就到libhwbinder里面了,有些跑题,就此打住。

Ril java部分

先画一个简单的类图,然后进一步分析。

这里写图片描述

Ril.java这个类还是继承了BaseCommands类,实现了CommandInterface接口。不过RILRequest,RILSender和RILReceiver这几个类都没有了; 取而代之的是RadioIndication和RadioResponse。

从这两个类的名字就可以看出RadioIndication用于处理modem主动上报的消息,而RadioResponse用于处理modem对AP侧request的response。

public class RadioResponse extends IRadioResponse.Stub {
......
}

public class RadioResponse extends IRadioResponse.Stub {
......
}

根据上面的类定义RadioResponse和RadioIndication这两个类都继承了Stub,这明显是为了用于进程间通信。

Ril.java对象是在开机时PhoneApp应用启动的过程中创建的,Ril.java的构造函数完成了初始化工作,包括RadioResponse,RadioIndication对象的创建以及HIDL service的获取。

private IRadio getRadioProxy(message result) {
        ......

        try {
            mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]);//获取Rild注册servcie的proxy,
            if (mRadioProxy != null) {
                mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
                        mRadioProxyCookie.incrementAndGet());
                mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication);//将RadioResponse和RadioIndication对象传递给service。
            } else {
                riljLoge("getRadioProxy: mRadioProxy == null");
            }
        } catch (RemoteException | runtimeexception e) {
            mRadioProxy = null;
            riljLoge("RadioProxy getService/setResponseFunctions: " + e);
        }
        ......

        return mRadioProxy;
    }

getRadioProxy方法获取了HIDL service,并将将RadioResponse和RadioIndication对象传递给service。经过binder传递后,这个两个对象的引用将分别被保存在RadioImpl::mRadioResponse和RadioImpl::mRadioIndication中, 后续用于modem给AP侧上报消息。

这里写图片描述

下面对于AP侧请求和Modem主动上报分别举一个例子:

AP 侧主动请求,以dial拨号为例.

首先看Ril.dial方法

    @Override
    public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
        IRadio radioProxy = getRadioProxy(result);//获取service proxy
        if (radioProxy != null) {
            //构造request 消息   Start
            RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,
                    mRILDefaultWorkSource);

            Dial dialInfo = new Dial();
            dialInfo.address = convertNullToemptyString(address);
            dialInfo.clir = clirMode;
            if (uusInfo != null) {
                UusInfo info = new UusInfo();
                info.uusType = uusInfo.getType();
                info.uusDcs = uusInfo.getDcs();
                info.uusData = new String(uusInfo.getUserData());
                dialInfo.uusInfo.add(info);
            }
            //构造request消息  End
            if (RILJ_LOGD) {
                // Do not log function arg for privacy
                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
            }

            try {
                radioProxy.dial(rr.mSerial, dialInfo);//调用dial方法
            } catch (RemoteException | RuntimeException e) {
                handleradioProxyExceptionForRR(rr, "dial", e);
            }
        }
    }

下面来看RadioImpl::dial函数:

Return<void> RadioImpl::dial(int32_t serial, const Dial& dialInfo) {
#if VDBG
    RLOGD("dial: serial %d", serial);
#endif
    RequestInfo *pRI = android::addRequestToList(serial, mSlotId, RIL_REQUEST_DIAL);//为RequestInfo类型分配了一块内存,并将地址给pRI, 这个是response时用的。
    if (pRI == NULL) {
        return Void();
    }
    /**解析参数,并保存在RIL_Dial类型的变量内  Start */
    RIL_Dial dial = {};
    RIL_UUS_Info uusInfo = {};
    int32_t sizeofDial = sizeof(dial);

    if (!copyHidlStringToRil(&dial.address, dialInfo.address, pRI)) {
        return Void();
    }
    dial.clir = (int) dialInfo.clir;

    if (dialInfo.uusInfo.size() != 0) {
        uusInfo.uusType = (RIL_UUS_Type) dialInfo.uusInfo[0].uusType;
        uusInfo.uusDcs = (RIL_UUS_DCS) dialInfo.uusInfo[0].uusDcs;

        if (dialInfo.uusInfo[0].uusData.size() == 0) {
            uusInfo.uusData = NULL;
            uusInfo.uusLength = 0;
        } else {
            if (!copyHidlStringToRil(&uusInfo.uusData, dialInfo.uusInfo[0].uusData, pRI)) {
                memsetAndFreeStrings(1, dial.address);
                return Void();
            }
            uusInfo.uusLength = dialInfo.uusInfo[0].uusData.size();
        }

        dial.uusInfo = &uusInfo;
    }
    /**解析参数,并保存在RIL_Dial类型的变量内  End */
    CALL_ONREQUEST(RIL_REQUEST_DIAL, &dial, sizeOfDial, pRI, mSlotId);

    memsetAndFreeStrings(2, dial.address, uusInfo.uusData);

    return Void();
}

CALL_ONREQUEST是一个宏定义:

#if defined(ANDROID_MULTI_SIM)
#define CALL_ONREQUEST(a, b, c, d, e) \
        s_vendorFunctions->onRequest((a), (b), (c), (d), ((RIL_SOCKET_ID)(e)))
#define CALL_ONSTATEREQUEST(a) s_vendorFunctions->onStateRequest((RIL_SOCKET_ID)(a))
#else
#define CALL_ONREQUEST(a, b, c, d, e) s_vendorFunctions->onRequest((a), (b), (c), (d))
#define CALL_ONSTATEREQUEST(a) s_vendorFunctions->onStateRequest()
#endif

s_vendorFunctions是vendor lib返回的函数指针,所以到这里也就调用了lib里面的相应函数。

当lib处理完之后会调用RIL_onRequestComplete,dialResponse函数会被调用。

int radio::dialResponse(int slotId,
                       int responseType, int serial, RIL_Errno e, void *response,
                       size_t responseLen) {
#if VDBG
    RLOGD("dialResponse: serial %d", serial);
#endif

    if (radioService[slotId]->mRadioResponse != NULL) {
        RadioResponseInfo responseInfo = {};
        populateResponseInfo(responseInfo, serial, responseType, e);
        Return<void> retStatus = radioService[slotId]->mRadioResponse->dialResponse(responseInfo);
        radioService[slotId]->checkReturnStatus(retStatus);
    } else {
        RLOGE("dialResponse: radioService[%d]->mRadioResponse == NULL", slotId);
    }

    return 0;
}

radioService[slotId]中的mRadioResponse 是RILJ在启动时放的回调,在RILJ对应的类是RadioResponse。

Modem主动上报:

在RILD daemon启动时在rild.c的main函数里调用了Ril_init函数,这给前边已经提到过了。

    ...
    funcs = rilInit(&s_rilEnv, argc, rilArgv);
    ...

调用是传递的参数s_rilEnv是rild.c的全局变量,该变量初始化情况如下:

static struct RIL_Env s_rilEnv = {
    RIL_onRequestComplete,
    RIL_onUnsolicitedResponse,
    RIL_requestTimedCallback,
    RIL_onRequestAck
};

将s_rilEnv的地址传进Ril_Init函数便是为了给modem上报消息提供调用接口。当modem有消息上报时,RIL_onUnsolicitedResponse函数会被调用。

下面的code为了突出上报功能将wakelock等部分省略了:

#if defined(ANDROID_MULTI_SIM)
extern "C"
void RIL_onUnsolicitedResponse(int unsolResponse, const void *data,
                                size_t datalen, RIL_SOCKET_ID socket_id)
#else
extern "C"
void RIL_onUnsolicitedResponse(int unsolResponse, const void *data,
                                size_t datalen)
#endif
{
    int unsolResponseIndex;
    int ret;
    bool shouldScheduleTimeout = false;
    RIL_SOCKET_ID soc_id = RIL_SOCKET_1;

#if defined(ANDROID_MULTI_SIM)
    soc_id = socket_id;
#endif


    if (s_registerCalled == 0) {
        // ignore RIL_onUnsolicitedResponse before RIL_register
        RLOGW("RIL_onUnsolicitedResponse called before RIL_register");
        return;
    }

    unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE;

    if ((unsolResponseIndex < 0)
        || (unsolResponseIndex >= (int32_t)NUM_ELEMS(s_unsolResponses))) {
        RLOGE("unsupported unsolicited response code %d", unsolResponse);
        return;
    }

    ...
    ret = s_unsolResponses[unsolResponseIndex].responseFunction(
            (int) soc_id, responseType, 0, RIL_E_SUCCESS, const_cast<void*>(data),
            datalen);

    ...
}

s_unsolResponses是ril.cpp的全局变量,使用头文件ril_unsol_commands.h初始化。该文件定义了每个unsolicited 消息对应的函数;RIL_onUnsolicitedResponse通过s_unsolResponses找到对应的函数,并调用。

ril_unsol_commands.h:

    {RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, radio::radioStateChangedInd, WAKE_PARTIAL},
    {RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, radio::callStateChangedInd, WAKE_PARTIAL},

以RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED为例,RIL_onUnsolicitedResponse会调用callStateChangedInd函数:

int radio::callStateChangedInd(int slotId,
                               int indicationType, int token, RIL_Errno e, void *response,
                               size_t responseLen) {
    if (radioService[slotId] != NULL && radioService[slotId]->mRadioIndication != NULL) {
#if VDBG
        RLOGD("callStateChangedInd");
#endif
        Return<void> retStatus = radioService[slotId]->mRadioIndication->callStateChanged(
                convertIntToRadioIndicationType(indicationType));
        radioService[slotId]->checkReturnStatus(retStatus);
    } else {
        RLOGE("callStateChangedInd: radioService[%d]->mRadioIndication == NULL", slotId);
    }

    return 0;
}

callStateChangedInd通过radioService[slotId]的mRadioIndication将消息传到java层,java层对应的类是RadioIndication.java。RadioIndication.callStateChanged会调用ril.java将消息进一步上传。

    public void callStateChanged(int indicationType) {
        mRil.processIndication(indicationType);

        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);

        mRil.mCallStateRegistrants.notifyRegistrants();
    }

到此结束!

相关阅读

Android 请求数据时,报错 "HttpException:HTTP 503 S

(1)场景http请求数据时,报以下异常  :同时,后台也检测不到接口请求的痕迹,但是有时再试一下又行了。(2)解决思路①后台连接数量可能限制,

HTTP 503 Service Temporarily Unavailable

HTTP 503服务暂时不可用retrofit2.adapter.rxjava.HttpException: HTTP 503 Service Temporarily Unavailable03-14 09:36:37.977

通过仿真和综合认识T触发器(Verilog HDL语言描述T触发

这个系列的博文已经写过了两篇,分别是通过仿真和综合认识D触发器(Verilog HDL语言描述D触发器)和通过仿真和综合认识JK触发器(Verilog

微软开源数据处理引擎 Trill,每天可分析万亿次事件

微软近日开源了数据处理引擎 Trill,它每天能够分析万亿次事件。 项目地址:https://github.com/Microsoft/trill 当下每毫秒处理大量

解决nginx 503 Service Temporarily Unavailable

503 Service Temporarily Unavailable 最近网站刷新后经常出现503 Service Temporarily Unavailable错误,有时有可以,联想到最近在

分享到:

栏目导航

推荐阅读

热门阅读