前言
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
在整个过程中,有几个关键问题:
- 上层是如何得知RILJ状态变化的;
- RILJ与RILD是怎么进行通信的?
- 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卡管理:
UiccController
,SubscriptionsController
等 - 数据链接管理:
DcTracker
,DctController
等 - Telephony 大管家:
PhoneBase
,GsmPhone
,PhoneProxy
等
以上代码主要位于两个目录:
/android/frameworks/opt/telephony/
(负责与RILD交互)/android/frameworks/base/telephony/
(对上层提供接口)
下面,以拨打电话的流程作为示例看一看RIL是如何发挥作用的。
示例: CALL流程
下图是一个MO(Mobile Originated) 通话流程简图:
- APP向
TelecomManager
发送拨号请求(关于TelecomManager
可以参考另一篇文章Android Telecom系统服务); TelecomManager
将通话请求发送给GsmPhone
;GsmPhone
继续将指令传递给GsmCallTracker
;GsmCallTracker
调用RILJ,RILJ将通话请求发送给RILD;RILD
接收到通话指令时,发送给CP;- CP发送给网络,MT(Mobile Terminal)收到通话后,告知网络,由网络将该信息传递给MO已将通话信息发送给MT了(就是手机发出嘟嘟声音的时候):通话状态由DIALING –> ALERTING;
- RILD收到通话状态变化的消息后,发送一个UNSOL_RESPONSE_CALL_STATE_CHANGED的消息给RILJ;
- RILJ通知
GsmCallTracker
通话状态变化了; GsmCallTracker
主动查询CALL状态:pollCallWhenSafe()
,确保得到的信息是对的,没有发生变化;- RILJ给RILD发送
getCurrentCalls()
的请求; - RILD获取到CALL状态后,上报给RILJ,再由RILJ返回结果给
GsmCallTracker
GsmCallTracker
得到确定的CALL状态后,通知GsmPhone
:notifyPreciseCallStateChanged()
;GsmPhone
将CALL状态变化的消息告知Telecom
系统服务;- 最后,
Telecom
系统服务发送CALL状态变化的广播给上层APP
到这一步后,通话并没有开始,如果MT接听了电话,则MO会收到CALL状态变化的信息,然后,才真正开始建立通话链接。