Android 8.0 RIL原始碼分析(一)
1.去電流程三中跟蹤到最後的時候可以看到其呼叫了RIL的dail方法
這裡繼續以此分析其從RIL到Modem的流程
@Override
public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
//獲取radio物件
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
//構建RIL請求訊息
RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,
mRILDefaultWorkSource);
Dial dialInfo = new Dial();
dialInfo.address = convertNullToEmptyString(address);
dialInfo.clir = clirMode;
if (uusInfo != null) {
UusInfo info = new UusInfo();
info.uusType = uusInfo.getType();
info.uusDcs = uusInfo.getDcs();
info.uusData = new String(uusInfo.getUserData());
dialInfo.uusInfo.add(info);
}
if (RILJ_LOGD) {
// Do not log function arg for privacy
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
}
try {
//呼叫dial方法
radioProxy.dial(rr.mSerial, dialInfo);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "dial", e);
}
}
}
1.1使用單例模式通過獲取Radio物件,可以看到其獲取的是HIDL的物件,並且設定的回覆方法mRadioResponse,可以參見RIL架構分析。
protected IRadio getRadioProxy(Message result) {
...
try {
//獲取HIDL的服務並設定setResponseFunctions
mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]);
if (mRadioProxy != null) {
mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
mRadioProxyCookie.incrementAndGet());
//設定mRadioResponse和mRadioIndication
mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication);
} else {
riljLoge("getRadioProxy: mRadioProxy == null");
}
} catch (RemoteException | RuntimeException e) {
mRadioProxy = null;
riljLoge("RadioProxy getService/setResponseFunctions: " + e);
}
...
}
可以看到其通過HIDL方式獲取到Radio物件後,進一步呼叫此物件的dial方法
繼續查詢遠端的Radio類
發現RadioImpl繼承了IRadio
struct RadioImpl : public V1_1::IRadio
2.在 ril_service 中實現了dial方法,繼續跟進此方法
Return<void> RadioImpl::dial(int32_t serial, const Dial& dialInfo) {
#if VDBG
RLOGD("dial: serial %d", serial);
#endif
//構建請求物件,Event側和reference測協定的統一格式,當從event測傳送到reference測時需要標準化為此物件
RequestInfo *pRI = android::addRequestToList(serial, mSlotId, RIL_REQUEST_DIAL);
if (pRI == NULL) {
return Void();
}
RIL_Dial dial = {};
RIL_UUS_Info uusInfo = {};
int32_t sizeOfDial = sizeof(dial);
if (!copyHidlStringToRil(&dial.address, dialInfo.address, pRI)) {
return Void();
}
dial.clir = (int) dialInfo.clir;
if (dialInfo.uusInfo.size() != 0) {
uusInfo.uusType = (RIL_UUS_Type) dialInfo.uusInfo[0].uusType;
uusInfo.uusDcs = (RIL_UUS_DCS) dialInfo.uusInfo[0].uusDcs;
if (dialInfo.uusInfo[0].uusData.size() == 0) {
uusInfo.uusData = NULL;
uusInfo.uusLength = 0;
} else {
if (!copyHidlStringToRil(&uusInfo.uusData, dialInfo.uusInfo[0].uusData, pRI)) {
memsetAndFreeStrings(1, dial.address);
return Void();
}
uusInfo.uusLength = dialInfo.uusInfo[0].uusData.size();
}
dial.uusInfo = &uusInfo;
}
//
CALL_ONREQUEST(RIL_REQUEST_DIAL, &dial, sizeOfDial, pRI, mSlotId);
memsetAndFreeStrings(2, dial.address, uusInfo.uusData);
return Void();
}
疑問點:為何沒有進入EventLoop迴圈????
在Android7.0上是通過Select獲取Socke傳送過來的訊息,然後通過EventLoop進行處理,最後呼叫到processCommandsCallback之後進行處理。和同事溝通後HIDL傳送訊息也會被Select獲取到,之後一路呼叫到ev->func(ev->fd, 0, ev->param); ,但在8.0上沒有processCommandsCallback處理,這裡筆者也未追溯到其如何進一步處理,後續會進一步進行分析。但從現有原始碼來看其是呼叫到了RadioImpl的dial方法進行處理。
3.繼續跟蹤CALL_ONREQUEST
搜尋CALL_ONREQUEST,可以看到其實際呼叫的是s_vendorFunctions->onRequest
#define CALL_ONREQUEST(a, b, c, d, e) s_vendorFunctions->onRequest((a), (b), (c), (d))
在registerService方法中看到s_vendorFunctions其實是傳進來的callbacks例項,即RIL_RadioFunctions物件,這裡具體分析可以見Android 8.0RIL框架分析。
在ril.c中繼續跟蹤OnRequest方法,回到之前rild.c的main中,我們看到其先呼叫rilInit進行初始化,之後返回了funcs,在傳如RIL_register進行註冊
跟進到reference-ril.c的 onRequest 方法
/**
* Call from RIL to us to make a RIL_REQUEST
*
* Must be completed with a call to RIL_onRequestComplete()
*
* RIL_onRequestComplete() may be called from any thread, before or after
* this function returns.
*
* Because onRequest function could be called from multiple different thread,
* we must ensure that the underlying at_send_command_* function
* is atomic.
*/
static void
onRequest (int request, void *data, size_t datalen, RIL_Token t)
{
ATResponse *p_response;
int err;
RLOGD("onRequest: %s", requestToString(request));
/* Ignore all requests except RIL_REQUEST_GET_SIM_STATUS
* when RADIO_STATE_UNAVAILABLE.
*/
if (sState == RADIO_STATE_UNAVAILABLE
&& request != RIL_REQUEST_GET_SIM_STATUS
) {
RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
return;
}
/* Ignore all non-power requests when RADIO_STATE_OFF
* (except RIL_REQUEST_GET_SIM_STATUS)
*/
if (sState == RADIO_STATE_OFF
&& !(request == RIL_REQUEST_RADIO_POWER
|| request == RIL_REQUEST_GET_SIM_STATUS)
) {
RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
return;
}
//可以看到只有兩種情況是單獨處理的,其他的在switch中處理
switch (request) {
...
case RIL_REQUEST_DIAL:
requestDial(data, datalen, t);
break;
...
}
}
4.繼續跟進requestDial方法
static void requestDial(void *data, size_t datalen __unused, RIL_Token t)
{
RIL_Dial *p_dial;
char *cmd;
const char *clir;
int ret;
p_dial = (RIL_Dial *)data;
switch (p_dial->clir) {
case 1: clir = "I"; break; /*invocation*/
case 2: clir = "i"; break; /*suppression*/
default:
case 0: clir = ""; break; /*subscription default*/
}
asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);
//傳送AT命令
ret = at_send_command(cmd, NULL);
free(cmd);
/* success or failure is ignored by the upper layer here.
it will call GET_CURRENT_CALLS and determine success that way */
//結束時呼叫
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
}
5.繼續跟蹤at_send_command
int at_send_command (const char *command, ATResponse **pp_outResponse)
{
int err;
err = at_send_command_full (command, NO_RESULT, NULL,
NULL, 0, pp_outResponse);
return err;
}
6.繼續跟進at_send_command_full
/**
* Internal send_command implementation
*
* timeoutMsec == 0 means infinite timeout
*/
static int at_send_command_full (const char *command, ATCommandType type,
const char *responsePrefix, const char *smspdu,
long long timeoutMsec, ATResponse **pp_outResponse)
{
int err;
bool inEmulator;
if (0 != pthread_equal(s_tid_reader, pthread_self())) {
/* cannot be called from reader thread */
return AT_ERROR_INVALID_THREAD;
}
inEmulator = isInEmulator();
if (inEmulator) {
pthread_mutex_lock(&s_writeMutex);
}
pthread_mutex_lock(&s_commandmutex);
//繼續傳送
err = at_send_command_full_nolock(command, type,
responsePrefix, smspdu,
timeoutMsec, pp_outResponse);
pthread_mutex_unlock(&s_commandmutex);
if (inEmulator) {
pthread_mutex_unlock(&s_writeMutex);
}
if (err == AT_ERROR_TIMEOUT && s_onTimeout != NULL) {
s_onTimeout();
}
return err;
}
7.繼續跟進at_send_command_full_nolock
/**
* Internal send_command implementation
* Doesn't lock or call the timeout callback
*
* timeoutMsec == 0 means infinite timeout
*/
static int at_send_command_full_nolock (const char *command, ATCommandType type,
const char *responsePrefix, const char *smspdu,
long long timeoutMsec, ATResponse **pp_outResponse)
{
int err = 0;
struct timespec ts;
if(sp_response != NULL) {
err = AT_ERROR_COMMAND_PENDING;
goto error;
}
//給modem傳送AT訊息
err = writeline (command);
if (err < 0) {
goto error;
}
s_type = type;
s_responsePrefix = responsePrefix;
s_smsPDU = smspdu;
//建立sp_respose作為迴應
sp_response = at_response_new();
if (timeoutMsec != 0) {
setTimespecRelative(&ts, timeoutMsec);
}
//傳送完後阻塞執行緒
while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
if (timeoutMsec != 0) {
//進入阻塞狀態,待另一個執行緒滿足s_commandcond後解除
err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);
} else {
err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
}
//超時結束
if (err == ETIMEDOUT) {
err = AT_ERROR_TIMEOUT;
goto error;
}
}
if (pp_outResponse == NULL) {
at_response_free(sp_response);
} else {
/* line reader stores intermediate responses in reverse order */
reverseIntermediates(sp_response);
//將回應發給請求的執行緒
*pp_outResponse = sp_response;
}
sp_response = NULL;
if(s_readerClosed > 0) {
err = AT_ERROR_CHANNEL_CLOSED;
goto error;
}
err = 0;
error:
clearPendingCommand();
return err;
}
可以看到這裡做了兩個重要的操作:
1.通過writeLine傳送資料給modem
2.阻塞當前執行緒等待modem迴應
8.這樣資料就傳送到了modem了
/**
* Sends string s to the radio with a \r appended.
* Returns AT_ERROR_* on error, 0 on success
*
* This function exists because as of writing, android libc does not
* have buffered stdio.
*/
static int writeline (const char *s)
{
size_t cur = 0;
size_t len = strlen(s);
ssize_t written;
if (s_fd < 0 || s_readerClosed > 0) {
return AT_ERROR_CHANNEL_CLOSED;
}
//AT命令的列印
RLOGD("AT> %s\n", s);
AT_DUMP( ">> ", s, strlen(s) );
/* the main string */
while (cur < len) {
do {
s_fd是modem和rilc的串列埠
written = write (s_fd, s + cur, len - cur);
} while (written < 0 && errno == EINTR);
if (written < 0) {
return AT_ERROR_GENERIC;
}
cur += written;
}
/* the \r */
//以r結尾
do {
written = write (s_fd, "\r" , 1);
} while ((written < 0 && errno == EINTR) || (written == 0));
if (written < 0) {
return AT_ERROR_GENERIC;
}
return 0;
}
相關推薦
Android 8.0 RIL原始碼分析(一)
1.去電流程三中跟蹤到最後的時候可以看到其呼叫了RIL的dail方法 這裡繼續以此分析其從RIL到Modem的流程 @Override public void dial(String address, int clirMode, UUSInfo
Android 6.0 Camera2 原始碼分析(1)不同的activity介面
Camera2中主要的activity activity都在AnroidManifest.xml中有註冊。我們先通過AndroidManifest.xml來大概的瞭解下都有哪些activity AndroidManifest.xml <?xm
Android 8.0系統原始碼分析--Binder程序間通訊(一)
開始我們的沉澱之路,老羅的書中第二章講的是Android HAL層的知識,而且直接自己實現了一個虛擬的freg驅動程式,後面的幾節是分別從native、java層如何訪問這個虛擬的驅動程式介面,我這裡沒有這樣的環境,所以就不分析這節了,第三章的智慧指標我對比8.0系統原
Android 8.0系統原始碼分析--openCamera(HAL)啟動過程原始碼分析
前面我們詳細分析了從應用層呼叫CameraManager的openCamera的方法來開啟相機的邏輯,上次的分析我們來到了CameraServer程序當中,但是還沒有真正看到open操作裝置節點來實現真正開啟的邏輯,遺留的問題也就是從frameworks\av\se
Android系統播放器MediaPlayer原始碼分析(一)
前言 對於MediaPlayer播放器的原始碼分析內容相對來說比較多,會從Java->JNI->C/C++慢慢分析,後面會慢慢更新。另外,部落格只作為自己學習記錄的一種方式,對於其他的不過多的評論。 MediaPlayerDemo public class MainA
spring boot 2.0 原始碼分析(一)
在學習spring boot 2.0原始碼之前,我們先利用spring initializr快速地建立一個基本的簡單的示例: 1.先從建立示例中的main函式開始讀起: package com.example; import org.springfra
Android recyclerview原始碼分析(一)
原始碼分析基於22.2.1版本 先預覽一下recyclerview 相關的類 今天先分析SortedList 和SortedListAdapterCallback 先看下這兩個類的用法 SortedList<Object> mDataList=new
Android SharedPreference 原始碼分析(一)
1. 前言 眾所周知,SharedPreferences是Android平臺上一個輕量級的儲存類,用來儲存應用的一些常用配置,比如Activity狀態,Activity暫停時,將此activity的狀態儲存到SharedPereferences中;當Activ
Android 8.0系統原始碼分析--Activity的視窗Window物件新增過程原始碼分析
這節我們來看一下Activity的視窗Window物件的建立過程,Activity作為Android提供的四大元件之首,我們之所以能非常簡單的使用它,就是因為它的建立過程中,framework為我們作了大量的初始化工作,包括它的視窗Window、視訊記憶體Surf
Android7.0去電流程原始碼分析(一)
2.去電從撥號盤介面有關撥號的部分由DialpadFragment.java實現,無論是單卡還是雙卡,當點選撥號按鍵時,最後都會呼叫handleDialButtonPressed方法進行處理,DialogFragmentCall_Action的活動Call_Ac
Android 8.0系統原始碼分析--開篇
好久沒寫部落格了,在這裡上班基本都加班,而且公司上不了外網,手機都不能帶進辦公室,所以就間斷了。昨天中午沒事翻老羅的CSDN部落格,忽然發現老羅的《Android系統原始碼情景分析 [羅昇陽著
phonegap原始碼分析(一)------ android
Phonegap已把原始碼提交到apache,成為一個非常受關注的開源框架cordova,它的跨平臺的特性有點當年Java的味道和勢頭,成為移動平臺上比較主流的解決方案。今日品味了一下它在android端的原始碼,看看它到底是如何結合native和web的。 首先我們總體上
AFNetworking3.1.0原始碼分析(一)整體框架和功能模組
簡介 1:基於系統NSURLSession類族封裝完成HPPT/HPPTS(GET,PUT,PSOT,DELEATE,HEAD)網路請求 2:擴充套件部分UIKit控制元件,比如擴充套件UIIMag
Android 8.0系統原始碼分析--startService啟動過程原始碼分析
作過android應用開發的同事都非常清楚,android提供了四個元件Activity、Service、BroastcastReceiver、ContentProvider,分別都有不同的作用,這也給我們的應用開發提供了非常大的幫助,因為這四大元件本身就已經處理了很
Android 8.0系統原始碼分析--相機createCaptureSession建立過程原始碼分析
上一次我們詳細分析了openCamera啟動過程的原始碼,從CameraServer程序建立了很多物件,比如CameraDeviceClient、Camera3Device、FrameProcessorBase,而真正開啟相機還是在驅動層中上電後才完成的,有時候真想
Android 8.0系統原始碼分析--openCamera啟動過程原始碼分析
說起Android相機的東西,從應用層的角度來看,基本就是四個重要的節點了:openCamera、createCaptureSession、preview、capture,最複雜的就是preview了,要理解preview,那麼就要求大家對And
Flume NG原始碼分析(一)基於靜態properties檔案的配置模組
日誌收集是網際網路公司的一個重要服務,Flume NG是Apache的頂級專案,是分散式日誌收集服務的一個開源實現,具有良好的擴充套件性,與其他很多開源元件可以無縫整合。搜了一圈發現介紹Flume NG的文章有不少,但是深入分析Flume NG原始碼的卻沒有。準備寫一個系列分析一下Flume NG的
GCC原始碼分析(一)——介紹與安裝
原文連結:http://blog.csdn.net/sonicling/article/details/6702031 上半年一直在做有關GCC和LD的專案,到現在還沒做完。最近幾天程式設計的那臺電腦壞了,所以趁此間隙寫一點相關的分析和
Android Hook框架adbi原始碼淺析(一)
adbi(The Android Dynamic Binary Instrumentation Toolkit)是一個Android平臺通用hook框架,基於動態庫注入與inline hook技術實現。該框架由兩個主要模組構成,1.hijack負責將動態庫注入到目標程序;2.libbase提供動態庫本身,它實