1. 程式人生 > >Android 8.0 RIL原始碼分析(一)

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系統原始碼分析--openCameraHAL啟動過程原始碼分析

     前面我們詳細分析了從應用層呼叫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提供動態庫本身,它實