当前位置: 移动技术网 > IT编程>移动开发>Android > Android RIL使用详解

Android RIL使用详解

2020年08月17日  | 移动技术网IT编程  | 我要评论
前言android作为一个通用的移动平台,其首要的功能就是通话、短信以及上网等通信功能。那么,从系统的角度来看,android究竟是怎么实现与网络的交互的了? 这篇文章里,就来看一看android中负

前言

android作为一个通用的移动平台,其首要的功能就是通话、短信以及上网等通信功能。那么,从系统的角度来看,android究竟是怎么实现与网络的交互的了? 这篇文章里,就来看一看android中负责通信功能的telephony中间层,通常也被称之为ril(radio interface layer)的具体实现原理与架构。

android手机要实现与网络端的通信,需要跨越两个层:

ril java(rilj):负责将上层app的通信请求发送给hal层;ril c++(rild): 系统守护进程,负责将rilj的请求命令发送给cp(communication processor)

什么是ril

简单的说,ril(radio interface layer),就是将应用程序的通信请求发送给cp的中间层,其包括两个部分,一个是java层rilj,一个是c++层(不妨看作是cp对应的hal层)rild。

rilj属于系统phone进程的一部分,随phone进程启动而加载;而rild守护进程是通过android的init进程进行加载的。

ril结构

下图是一个android ril的一个结构图。整个通信过程有四个层:

  • 最上层的是应用程序,如通话,短信以及sim卡管理,它们主要负责将用户的指令发送到ril framework(以后统称rilj);
  • rilj为上层提供了通用的api,如telephonymanager(包括通话,网络状态; subscriptionmanager(卡状态)以及smsmanager等,同时rilj还负责维持与rild的通信,并将上层的请求发送给rild;
  • rild是系统的守护进程,对于支持通话功能的移动平台是必不可少的。rild的功能主要功能是将rilj发送过来的请求继续传递给cp,同时会及时将cp的状态变化发送给rilj;
  • linux驱动层:kernel驱动层接受到数据后,将指令传给cp,最后由cp发送给网络端,等网络返回结果后,cp将传回给rild;

rilj与rild(rild与cp的通信)都是通过一个个消息进行数据传递。消息主要分两种:一种是rilj主动发送的请求(solicited),常见的有ril_request_get_sim_status(获取sim卡状态),ril_request_dial(拨打电话),ril_request_send_sms(发送短信),ril_request_get_current_calls(获取当前通话状态),ril_request_voice_registration_state(获取网络状态); 另一种则是从cp主动上报给ril的消息(unsolicited),如网络状态发生变化时,cp会上报ril_unsol_response_voice_network_state_changed,有新短信时,会上报ril_unsol_response_new_sms,有来电时会上报ril_unsol_call_ring。

ril相关的请求命令与数据结构都定义在/android/hardware/ril/include/telephony/ril.h

在整个过程中,有几个关键问题:

  1. 上层是如何得知rilj状态变化的;
  2. rilj与rild是怎么进行通信的?
  3. riljd与cp又是如何进行通信的?

围绕这三个问题点,我们来看一下具体的细节。

上层如何得知rilj状态变化

为方便上层实时监听网络状态、通话状态以及cp的状态变化,ril提供了一个专门的监听接口iphonestatelistener.aidl,上层需要监听上述状态变化时,只需要实现上述接口,并在android系统服务telephonyregistry中对上述接口实现进行注册:

public void listen(string pkgfordebug, iphonestatelistener callback, int events, boolean notifynow);

另外,也可以在telephonymanager中对ril状态进行监听:

 public void listen(phonestatelistener listener, int events)

源代码:/android/frameworks/base/telephony/java/com/android/internal/telephony/iphonestatelistener.aidl

oneway interface iphonestatelistener {
 void onservicestatechanged(in servicestate servicestate);
 void onsignalstrengthchanged(int asu);
 void onmessagewaitingindicatorchanged(boolean mwi);
 void oncallforwardingindicatorchanged(boolean cfi);

 // we use bundle here instead of celllocation so it can get the right subclass
 void oncelllocationchanged(in bundle location);
 void oncallstatechanged(int state, string incomingnumber);
 void ondataconnectionstatechanged(int state, int networktype);
 void ondataactivity(int direction);
 void onsignalstrengthschanged(in signalstrength signalstrength);
 void onotaspchanged(in int otaspmode);
 void oncellinfochanged(in list<cellinfo> cellinfo);
 void onprecisecallstatechanged(in precisecallstate callstate);
 void onprecisedataconnectionstatechanged(in precisedataconnectionstate dataconnectionstate);
 void ondataconnectionrealtimeinfochanged(in dataconnectionrealtimeinfo dcrtinfo);
 void onvolteservicestatechanged(in volteservicestate ltestate);
 void onoemhookrawevent(in byte[] rawdata);
 void oncarriernetworkchange(in boolean active);
 void onfdnupdated();

 void onvoiceradiobearerhostatechanged(int state);
 }

rilj与rild如何通信

rilj在创建过程中,会启动两个线程:rilsender和rilreceiver,rilsender负责将指令发送给rild,而rilreceiver则负责从读取从rild发送过来的数据。rilj与rild的通信通道就是在rilreceiver中建立起来的。

我们来看一看rilreciver的代码:

class rilreceiver implements runnable {
 byte[] buffer;

 rilreceiver() {
 ...
 @override
 public void
 run() {
 int retrycount = 0;
 string rilsocket = "rild";

 // 尝试与rild建立连接
 try {for (;;) {
 localsocket s = null;
 localsocketaddress l;

 if (minstanceid == null || minstanceid == 0 ) {
  rilsocket = socket_name_ril[0];
 } else {
  rilsocket = socket_name_ril[minstanceid];
 }

 try {
  s = new localsocket();
  l = new localsocketaddress(rilsocket,
  localsocketaddress.namespace.reserved);
  s.connect(l);
 } catch (ioexception ex){
  ...
  // don't print an error message after the the first time
  // or after the 8th time

  if (retrycount == 8) {
  rlog.e (rilj_log_tag,
  "couldn't find '" + rilsocket
  + "' socket after " + retrycount
  + " times, continuing to retry silently");
  } else if (retrycount >= 0 && retrycount < 8) {
  rlog.i (rilj_log_tag,
  "couldn't find '" + rilsocket
  + "' socket; retrying after timeout");
  }
  ...

  retrycount++;
  continue;
 }

 retrycount = 0;
 msocket = s;

 // 从socket读取数据
 int length = 0;
 try {
  inputstream is = msocket.getinputstream();

  for (;;) {
  parcel p;

  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);
  p.recycle();
  }
 } catch (java.io.ioexception ex) {
  rlog.i(rilj_log_tag, "'" + rilsocket + "' socket closed",
  ex);
 } catch (throwable tr) {
  rlog.e(rilj_log_tag, "uncaught exception read length=" + length +
  "exception:" + tr.tostring());
 }

 //无法读取数据,将cp状态设置为不可用
 setradiostate (radiostate.radio_unavailable);
 ...
 msocket = null;
 rilrequest.resetserial();

 // clear request list on close
 clearrequestlist(radio_not_available, false);
 }} catch (throwable tr) {
 rlog.e(rilj_log_tag,"uncaught exception", tr);
 }
 }
 }

rilreceiver启动时,会建立一个unix domain socket(localsocket,kernel层对应/dev/socket/rild),与rild进行通信,然后一直从socket中读取数据,并将数据传给上层。连接成功后,rild会发送一个消息给rilj,表示连接成功了,这样rilj就可以将请求数据发送给rild,进行通信了。

rild与cp如何进行通信

rild与cp(可以看做是两个运行在不同cpu上的进程通信)交换数据方式一般有两种情况。如果ap与cp集中在一个芯片上,如高通的平台就是将ap与cp集中在一块芯片上,这时通常采用共享内存的方式实现跨进程通信;而如果不是在同一块芯片,而是ap与cp分别采用不同厂商的平台,则一般采用字符设备(character devices) 进行通信。总的说来,共享内存的方式在速度上要优于字符设备。

接下来,主要介绍下rilj部分的代码结构。

rilj代码结构

ril framework (rilj)的代码按照功能来划分的话,主要有以下几个组成部分:

  • 管理网络状态(信号强度,网络注册状态等):servicestatetracker等;
  • 通话管理(拨号,接听,呼叫等待等):callmanager,gsmcalltracker等
  • sms短信接收发送:inboundsmshandler,smsdispater等
  • sim卡管理:uicccontroller,subscriptionscontroller等
  • 数据链接管理:dctracker,dctcontroller等
  • telephony 大管家:phonebase,gsmphone,phoneproxy等

以上代码主要位于两个目录:

  • /android/frameworks/opt/telephony/(负责与rild交互)
  • /android/frameworks/base/telephony/(对上层提供接口)

下面,以拨打电话的流程作为示例看一看ril是如何发挥作用的。

示例:call流程

下图是一个mo(mobile originated) 通话流程简图:

  1. app向telecommanager发送拨号请求(关于telecommanager可以参考另一篇文章android telecom系统服务);
  2. telecommanager将通话请求发送给gsmphone;
  3. gsmphone继续将指令传递给gsmcalltracker;
  4. gsmcalltracker调用rilj,rilj将通话请求发送给rild;
  5. rild接收到通话指令时,发送给cp;
  6. cp发送给网络,mt(mobile terminal)收到通话后,告知网络,由网络将该信息传递给mo已将通话信息发送给mt了(就是手机发出嘟嘟声音的时候):通话状态由dialing –> alerting;
  7. rild收到通话状态变化的消息后,发送一个unsol_response_call_state_changed的消息给rilj;
  8. rilj通知gsmcalltracker通话状态变化了;
  9. gsmcalltracker主动查询call状态:pollcallwhensafe(),确保得到的信息是对的,没有发生变化;
  10. rilj给rild发送getcurrentcalls()的请求;
  11. rild获取到call状态后,上报给rilj,再由rilj返回结果给gsmcalltracker
  12. gsmcalltracker得到确定的call状态后,通知gsmphone:notifyprecisecallstatechanged();
  13. gsmphone将call状态变化的消息告知telecom系统服务;
  14. 最后,telecom系统服务发送call状态变化的广播给上层app

到这一步后,通话并没有开始,如果mt接听了电话,则mo会收到call状态变化的信息,然后,才真正开始建立通话链接。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网