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,它每天能够分析万亿次事件。 项目地址:https://github.com/Microsoft/trill 当下每毫秒处理大量
解决nginx 503 Service Temporarily Unavailable
503 Service Temporarily Unavailable 最近网站刷新后经常出现503 Service Temporarily Unavailable错误,有时有可以,联想到最近在