• 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;

    RIL Architecture

    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的代码:

     1  class RILReceiver implements Runnable {
     2         byte[] buffer;
     3 
     4         RILReceiver() {
     5         ...
     6         @Override
     7         public void
     8         run() {
     9             int retryCount = 0;
    10             String rilSocket = "rild";
    11 
    12             // 尝试与RILD建立连接
    13             try {for (;;) {
    14                 LocalSocket s = null;
    15                 LocalSocketAddress l;
    16 
    17                 if (mInstanceId == null || mInstanceId == 0 ) {
    18                     rilSocket = SOCKET_NAME_RIL[0];
    19                 } else {
    20                     rilSocket = SOCKET_NAME_RIL[mInstanceId];
    21                 }
    22 
    23                 try {
    24                     s = new LocalSocket();
    25                     l = new LocalSocketAddress(rilSocket,
    26                             LocalSocketAddress.Namespace.RESERVED);
    27                     s.connect(l);
    28                 } catch (IOException ex){
    29                     ...
    30                     // don't print an error message after the the first time
    31                     // or after the 8th time
    32 
    33                     if (retryCount == 8) {
    34                         Rlog.e (RILJ_LOG_TAG,
    35                             "Couldn't find '" + rilSocket
    36                             + "' socket after " + retryCount
    37                             + " times, continuing to retry silently");
    38                     } else if (retryCount >= 0 && retryCount < 8) {
    39                         Rlog.i (RILJ_LOG_TAG,
    40                             "Couldn't find '" + rilSocket
    41                             + "' socket; retrying after timeout");
    42                     }
    43                     ...
    44 
    45                     retryCount++;
    46                     continue;
    47                 }
    48 
    49                 retryCount = 0;
    50                 mSocket = s;
    51 
    52                 // 从socket读取数据
    53                 int length = 0;
    54                 try {
    55                     InputStream is = mSocket.getInputStream();
    56 
    57                     for (;;) {
    58                         Parcel p;
    59 
    60                         length = readRilMessage(is, buffer);
    61 
    62                         if (length < 0) {
    63                             // End-of-stream reached
    64                             break;
    65                         }
    66 
    67                         p = Parcel.obtain();
    68                         p.unmarshall(buffer, 0, length);
    69                         p.setDataPosition(0);
    70 
    71                         processResponse(p);
    72                         p.recycle();
    73                     }
    74                 } catch (java.io.IOException ex) {
    75                     Rlog.i(RILJ_LOG_TAG, "'" + rilSocket + "' socket closed",
    76                           ex);
    77                 } catch (Throwable tr) {
    78                     Rlog.e(RILJ_LOG_TAG, "Uncaught exception read length=" + length +
    79                         "Exception:" + tr.toString());
    80                 }
    81 
    82                 //无法读取数据,将CP状态设置为不可用
    83                 setRadioState (RadioState.RADIO_UNAVAILABLE);
    84                 ...
    85                 mSocket = null;
    86                 RILRequest.resetSerial();
    87 
    88                 // Clear request list on close
    89                 clearRequestList(RADIO_NOT_AVAILABLE, false);
    90             }} catch (Throwable tr) {
    91                 Rlog.e(RILJ_LOG_TAG,"Uncaught exception", tr);
    92             }
    93         }
    94     }

    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卡管理: UiccControllerSubscriptionsController
    • 数据链接管理: DcTracker,DctController
    • Telephony 大管家: PhoneBase,GsmPhone,PhoneProxy

    Android RIL Structure

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

    • /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状态后,通知GsmPhonenotifyPreciseCallStateChanged();
    13. GsmPhone将CALL状态变化的消息告知Telecom系统服务;
    14. 最后,Telecom系统服务发送CALL状态变化的广播给上层APP

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

    MO Call Process

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wang2119/article/details/53232022
  • 相关阅读:
    阮一峰的网络日志 算法
    Problem 2184 逆序数还原
    vs项目中使用c++调用lua
    多例模式
    关于O_APPEND模式write的原子性
    清醒
    编译时,遇到Couldn&#39;t build player because of unsupported data on target platform的解决方式
    把JavaScript和CSS放到外部文件里
    SSM框架——具体整合教程(Spring+SpringMVC+MyBatis)
    MMORPG网页游戏斩仙录全套源代码(服务端+client)
  • 原文地址:https://www.cnblogs.com/ricks/p/9497403.html
Copyright © 2020-2023  润新知