Android RIL使用詳解
前言
Android作為一個通用的移動平臺,其首要的功能就是通話、短信以及上網(wǎng)等通信功能。那么,從系統(tǒng)的角度來看,Android究竟是怎么實(shí)現(xiàn)與網(wǎng)絡(luò)的交互的了? 這篇文章里,就來看一看Android中負(fù)責(zé)通信功能的Telephony中間層,通常也被稱之為RIL(Radio Interface Layer)的具體實(shí)現(xiàn)原理與架構(gòu)。
Android手機(jī)要實(shí)現(xiàn)與網(wǎng)絡(luò)端的通信,需要跨越兩個層:
RIL Java(RILJ):負(fù)責(zé)將上層APP的通信請求發(fā)送給HAL層;RIL C++(RILD): 系統(tǒng)守護(hù)進(jìn)程,負(fù)責(zé)將RILJ的請求命令發(fā)送給CP(Communication Processor)
什么是RIL
簡單的說,RIL(Radio Interface Layer),就是將應(yīng)用程序的通信請求發(fā)送給CP的中間層,其包括兩個部分,一個是Java層RILJ,一個是C++層(不妨看作是CP對應(yīng)的HAL層)RILD。
RILJ屬于系統(tǒng)Phone進(jìn)程的一部分,隨Phone進(jìn)程啟動而加載;而RILD守護(hù)進(jìn)程是通過Android的Init進(jìn)程進(jìn)行加載的。
RIL結(jié)構(gòu)
下圖是一個Android RIL的一個結(jié)構(gòu)圖。整個通信過程有四個層:
- 最上層的是應(yīng)用程序,如通話,短信以及SIM卡管理,它們主要負(fù)責(zé)將用戶的指令發(fā)送到RIL Framework(以后統(tǒng)稱RILJ);
- RILJ為上層提供了通用的API,如TelephonyManager(包括通話,網(wǎng)絡(luò)狀態(tài); SubscriptionManager(卡狀態(tài))以及SmsManager等,同時RILJ還負(fù)責(zé)維持與RILD的通信,并將上層的請求發(fā)送給RILD;
- RILD是系統(tǒng)的守護(hù)進(jìn)程,對于支持通話功能的移動平臺是必不可少的。RILD的功能主要功能是將RILJ發(fā)送過來的請求繼續(xù)傳遞給CP,同時會及時將CP的狀態(tài)變化發(fā)送給RILJ;
- Linux驅(qū)動層:kernel驅(qū)動層接受到數(shù)據(jù)后,將指令傳給CP,最后由CP發(fā)送給網(wǎng)絡(luò)端,等網(wǎng)絡(luò)返回結(jié)果后,CP將傳回給RILD;

RILJ與RILD(RILD與CP的通信)都是通過一個個消息進(jìn)行數(shù)據(jù)傳遞。消息主要分兩種:一種是RILJ主動發(fā)送的請求(solicited),常見的有RIL_REQUEST_GET_SIM_STATUS(獲取SIM卡狀態(tài)),RIL_REQUEST_DIAL(撥打電話),RIL_REQUEST_SEND_SMS(發(fā)送短信),RIL_REQUEST_GET_CURRENT_CALLS(獲取當(dāng)前通話狀態(tài)),RIL_REQUEST_VOICE_REGISTRATION_STATE(獲取網(wǎng)絡(luò)狀態(tài)); 另一種則是從CP主動上報給RIL的消息(unsolicited),如網(wǎng)絡(luò)狀態(tài)發(fā)生變化時,CP會上報RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED,有新短信時,會上報RIL_UNSOL_RESPONSE_NEW_SMS,有來電時會上報RIL_UNSOL_CALL_RING。
RIL相關(guān)的請求命令與數(shù)據(jù)結(jié)構(gòu)都定義在/android/hardware/ril/include/telephony/ril.h
在整個過程中,有幾個關(guān)鍵問題:
- 上層是如何得知RILJ狀態(tài)變化的;
- RILJ與RILD是怎么進(jìn)行通信的?
- RILJD與CP又是如何進(jìn)行通信的?
圍繞這三個問題點(diǎn),我們來看一下具體的細(xì)節(jié)。
上層如何得知RILJ狀態(tài)變化
為方便上層實(shí)時監(jiān)聽網(wǎng)絡(luò)狀態(tài)、通話狀態(tài)以及CP的狀態(tài)變化,RIL提供了一個專門的監(jiān)聽接口IPhoneStateListener.aidl,上層需要監(jiān)聽上述狀態(tài)變化時,只需要實(shí)現(xiàn)上述接口,并在Android系統(tǒng)服務(wù)TelephonyRegistry中對上述接口實(shí)現(xiàn)進(jìn)行注冊:
public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow);
另外,也可以在TelephonyManager中對RIL狀態(tài)進(jìn)行監(jiān)聽:
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在創(chuàng)建過程中,會啟動兩個線程:RILSender和RILReceiver,RILSender負(fù)責(zé)將指令發(fā)送給RILD,而RILReceiver則負(fù)責(zé)從讀取從RILD發(fā)送過來的數(shù)據(jù)。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讀取數(shù)據(jù)
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());
}
//無法讀取數(shù)據(jù),將CP狀態(tài)設(shè)置為不可用
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層對應(yīng)/dev/socket/rild),與RILD進(jìn)行通信,然后一直從socket中讀取數(shù)據(jù),并將數(shù)據(jù)傳給上層。連接成功后,RILD會發(fā)送一個消息給RILJ,表示連接成功了,這樣RILJ就可以將請求數(shù)據(jù)發(fā)送給RILD,進(jìn)行通信了。
RILD與CP如何進(jìn)行通信
RILD與CP(可以看做是兩個運(yùn)行在不同CPU上的進(jìn)程通信)交換數(shù)據(jù)方式一般有兩種情況。如果AP與CP集中在一個芯片上,如高通的平臺就是將AP與CP集中在一塊芯片上,這時通常采用共享內(nèi)存的方式實(shí)現(xiàn)跨進(jìn)程通信;而如果不是在同一塊芯片,而是AP與CP分別采用不同廠商的平臺,則一般采用字符設(shè)備(character devices) 進(jìn)行通信??偟恼f來,共享內(nèi)存的方式在速度上要優(yōu)于字符設(shè)備。
接下來,主要介紹下RILJ部分的代碼結(jié)構(gòu)。
RILJ代碼結(jié)構(gòu)
RIL Framework (RILJ)的代碼按照功能來劃分的話,主要有以下幾個組成部分:
- 管理網(wǎng)絡(luò)狀態(tài)(信號強(qiáng)度,網(wǎng)絡(luò)注冊狀態(tài)等):ServiceStateTracker等;
- 通話管理(撥號,接聽,呼叫等待等):CallManager,GsmCallTracker等
- SMS短信接收發(fā)送:InboundSMSHandler,SmsDispater等
- SIM卡管理:UiccController,SubscriptionsController等
- 數(shù)據(jù)鏈接管理:DcTracker,DctController等
- Telephony 大管家:PhoneBase,GsmPhone,PhoneProxy等

以上代碼主要位于兩個目錄:
- /android/frameworks/opt/telephony/(負(fù)責(zé)與RILD交互)
- /android/frameworks/base/telephony/(對上層提供接口)
下面,以撥打電話的流程作為示例看一看RIL是如何發(fā)揮作用的。
示例:CALL流程
下圖是一個MO(Mobile Originated) 通話流程簡圖:

- APP向TelecomManager發(fā)送撥號請求(關(guān)于TelecomManager可以參考另一篇文章Android Telecom系統(tǒng)服務(wù));
- TelecomManager將通話請求發(fā)送給GsmPhone;
- GsmPhone繼續(xù)將指令傳遞給GsmCallTracker;
- GsmCallTracker調(diào)用RILJ,RILJ將通話請求發(fā)送給RILD;
- RILD接收到通話指令時,發(fā)送給CP;
- CP發(fā)送給網(wǎng)絡(luò),MT(Mobile Terminal)收到通話后,告知網(wǎng)絡(luò),由網(wǎng)絡(luò)將該信息傳遞給MO已將通話信息發(fā)送給MT了(就是手機(jī)發(fā)出嘟嘟聲音的時候):通話狀態(tài)由DIALING –> ALERTING;
- RILD收到通話狀態(tài)變化的消息后,發(fā)送一個UNSOL_RESPONSE_CALL_STATE_CHANGED的消息給RILJ;
- RILJ通知GsmCallTracker通話狀態(tài)變化了;
- GsmCallTracker主動查詢CALL狀態(tài):pollCallWhenSafe(),確保得到的信息是對的,沒有發(fā)生變化;
- RILJ給RILD發(fā)送getCurrentCalls()的請求;
- RILD獲取到CALL狀態(tài)后,上報給RILJ,再由RILJ返回結(jié)果給GsmCallTracker
- GsmCallTracker得到確定的CALL狀態(tài)后,通知GsmPhone:notifyPreciseCallStateChanged();
- GsmPhone將CALL狀態(tài)變化的消息告知Telecom系統(tǒng)服務(wù);
- 最后,Telecom系統(tǒng)服務(wù)發(fā)送CALL狀態(tài)變化的廣播給上層APP
到這一步后,通話并沒有開始,如果MT接聽了電話,則MO會收到CALL狀態(tài)變化的信息,然后,才真正開始建立通話鏈接。
相關(guān)文章
Android中TextView實(shí)現(xiàn)分段顯示不同顏色的字符串
在做項(xiàng)目的時候,遇到過一行文字有兩種顏色。在菜鳥的時候直接會想到用多個TextView來實(shí)現(xiàn),所以下面這篇文章主要給大家介紹了關(guān)于Android中TextView如何實(shí)現(xiàn)分段顯示不同顏色字符串的相關(guān)資料,需要的朋友可以參考下。2017-12-12
Android實(shí)現(xiàn)簡單斷點(diǎn)續(xù)傳和下載到本地功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡單斷點(diǎn)續(xù)傳和下載到本地功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11
Android編程實(shí)現(xiàn)短信收發(fā)及語音播報提示功能示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)短信收發(fā)及語音播報提示功能,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)短信的接收、發(fā)送以及相應(yīng)的語音播報提示功能相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
Android圖片異步加載框架Android-Universal-Image-Loader
這篇文章主要介紹了Android圖片異步加載框架Android-Universal-Image-Loader,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
Popupwindow 的簡單實(shí)用案例(顯示在控件下方)
下面小編就為大家?guī)硪黄狿opupwindow 的簡單實(shí)用案例(顯示在控件下方)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04
Android自定義控件EditText實(shí)現(xiàn)清除和抖動功能
這篇文章主要為大家詳細(xì)介紹了Android自定義控件EditText實(shí)現(xiàn)清除和抖動功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12
Android App中用Handler實(shí)現(xiàn)ViewPager頁面的自動切換
這篇文章主要介紹了Android App中用Handler實(shí)現(xiàn)ViewPager頁面的自動切換的方法,類似于相冊自動播放,主要是切換后要提示當(dāng)前頁面所在的位置,需要的朋友可以參考下2016-05-05
Android App后臺服務(wù)報告工作狀態(tài)實(shí)例
這篇文章主要介紹了Android App后臺服務(wù)報告工作狀態(tài)實(shí)例,使用LocalBroadcastManager發(fā)送和接收狀態(tài),需要的朋友可以參考下2014-06-06
Android 系統(tǒng)實(shí)現(xiàn)多種開機(jī)動畫和logo切換功能
這篇文章主要介紹了android 系統(tǒng)實(shí)現(xiàn)多種開機(jī)動畫和logo切換功能,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-12-12

