Android RIL使用详解

前言

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状态变化的信息,然后,才真正开始建立通话链接。

到此这篇关于Android RIL使用详解的文章就介绍到这了,更多相关Android RIL内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Android4.4 RIL短信接收流程分析

    最近有客户反馈Android接收不到短信,于是一头扎进RIL里面找原因.最后发现不是RIL的问题,而是BC72上报 短信的格式不对,AT+CNMA=1无作用等几个小问题导致的.尽管问题不在RIL,但总算把RIL短信接收流程搞清楚了. 接收到新信息的log: D/ATC ( 1269): AT< +CMT:,27 D/ATC ( 1268): AT< 0891683108705505F0040d91683117358313f500009101329154922307ea31da2c36a301

  • Android RIL使用详解

    前言 Android作为一个通用的移动平台,其首要的功能就是通话.短信以及上网等通信功能.那么,从系统的角度来看,Android究竟是怎么实现与网络的交互的了? 这篇文章里,就来看一看Android中负责通信功能的Telephony中间层,通常也被称之为RIL(Radio Interface Layer)的具体实现原理与架构. Android手机要实现与网络端的通信,需要跨越两个层: RIL Java(RILJ):负责将上层APP的通信请求发送给HAL层:RIL C++(RILD): 系统守护进

  • Kotlin开发Android应用实例详解

    Kotlin开发Android应用实例详解 相关文章:关于Kotlin语言的基础介绍: http://www.jb51.net/article/114086.htm 我们简单的知道了Kotlin这门新语言的优势,也接触了一些常见的语法及其简单的使用,相信你会对它有浓厚的兴趣,暂且理解为对它感兴趣吧,哈哈哈.那么,我们该如何在Android中应用这门新的语言呢?今天的这篇文章带你学习使用Kotlin开发Android应用,并对比我们传统语言Java,让你真真切切的感受到他的美和优雅. 配置 项目g

  • Android 文件操作详解及简单实例

     Android 文件操作详解 Android 的文件操作说白了就是Java的文件操作的处理.所以如果对Java的io文件操作比较熟悉的话,android的文件操作就是小菜一碟了.好了,话不多说,开始今天的正题吧. 先从一个小项目入门吧 首先是一个布局文件,这一点比较的简单,那就直接上代码吧. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="htt

  • Android中SharedPreference详解及简单实例

     Android中SharedPreference详解 SharedPreference是Android提供的一种轻量级的数据存储方式,主要用来存储一些简单的配置信息,例如,默认欢迎语,登录用户名和密码等.其以键值对的方式存储,使得我们能很方便进行读取和存入. SharedPreference 文件保存在/data/data/<package name>/shared_prefs 路径下(如/data/data/com.android.alarmclock/shared_prefs/com.a

  • Android 中Seekbar详解及简单实例

    Android 中Seekbar详解及简单实例 做到音频播放和音乐播放时,大多数都要用到Seekbar.现在我先简单介绍下Seekbar的几个重要属性. android:max 设置值的大小 . android:thumb="@drawable/" 显示的那个可拖动图标,如果没有设置该参数则为系统默认,如果自己需要重新定义,则将自己需要的图标存放在资源目录 /res/drawable下,然后调用即可. android:thumbOffset 拖动图标的偏量值,可以让拖动图标超过bar的

  • Android LayoutInflater.inflate()详解及分析

    Android  LayoutInflater.inflate()详解 深入理解LayoutInflater.inflate() 由于我们很容易习惯公式化的预置代码,有时我们会忽略很优雅的细节.LayoutInflater以及它在Fragment的onCreateView()中填充View的方式带给我的就是这样的感受.这个类用于将XML文件转换成相对应的ViewGroup和控件Widget.我尝试在Google官方文档与网络上其他讨论中寻找有关的说明,而后发现许多人不但不清楚LayoutInfl

  • Android AOP 注解详解及简单使用实例(三)

    Android  注解 相关文章: Android AOP注解Annotation详解(一) Android AOP之注解处理解释器详解(二) Android AOP 注解详解及简单使用实例(三) 一.简介 在Android 里面 注解主要用来干这么几件事: 和编译器一起给你一些提示警告信息. 配合一些ide 可以更加方便快捷 安全有效的编写Java代码.谷歌出的support-annotations这个库 就是主要干这个的. 和反射一起 提供一些类似于spring 可配置的功能,方便简洁. 二

  • Android 资源 id详解及的动态获取

    Android 资源 id详解 我们平时获取资源是通过 findViewById 方法进行的,比如我们常在onCreate方法中使用这样的语句: btnChecked=(ImageView)findViewById(R.id.imgCheck); findViewById是我们获取layout中各种View 对象比如按钮.标签.ListView和ImageView的便利方法.顾名思义,它需要一个int参数:资源id. 资源id非常有用.Android回自动为每个位于res目录下的资源分配id,包

  • Android安装apk文件并适配Android 7.0详解

    Android安装apk文件并适配Android 7.0详解 首先在AndroidManifest.xml文件,activity同级节点注册provider: <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.file_provider" android:exported="false"

  • Android ViewFlipper的详解及实例

    Android ViewFlipper的详解 前言: View Flipper,是ViewAnimator的子类,而ViewAnimator又是继承自FrameLayout,而FrameLayout就是平时基本上只显示一个子视图的布局,由于FrameLayout下不好确定子视图的位置,所以很多情况下子视图之前存在相互遮挡,这样就造成了很多时候我们基本上只要求FrameLayout显示一个子视图,然后通过某些控制来实现切换.正好,ViewFlipper帮我们实现了这个工作,我们需要做的就是,选择恰

随机推荐