Android telephony整體結構簡要介紹(RILJ、RILD詳解) 一、前言 二、RIL詳細介紹 三、寫在最後

一、前言

本文主要簡要介紹Android整個通信模塊結構信息,並詳細介紹RIL(modem和上層之間的運作方式)。

1.1、基礎概念

1、通信整體構成
首先從硬件層面上來說,手機的設計都是手機芯片+信號處理模塊,信號處理模塊可以處理modem(調制解調器)+RF(射頻)+UICC(通用集成電路卡,即電話卡) 。

2、AP和BP
其中Android系統、UI和應用程序運行在手機芯片上,我們稱之爲AP端
手機視頻通訊控制運行在信號處理模塊上的芯片上,我們稱之爲BP端
這麼設計核心優勢主要是不管操作系統怎麼變,通信模塊都是相同的。數據都不會出錯,增加穩定性

1.2、相關代碼

1、大部分源碼aosp都有,由於各個廠商定製的不同,在芯片廠商提供給手機廠商的原始代碼和aosp的路徑還是有部分差異和定製。以下代碼路徑以MTK方案舉例。MTK部分型號芯片也在vendor路徑下單獨提供了實現的代碼。但這不影響我們學習通信模塊。這部分表格中我就沒有列出來了。
2、本文講解時只列舉核心代碼行,需要讀者自行結合源碼閱讀
3、本文講解以上層爲主。底層c/c++暫不贅述

層級 路徑 文件
Apps /packages/apps/Dialer Dialer.apk
/packages/apps/Messaging Messaging.apk
/packages/apps/Contacts Contacts.apk
Providers /packages/providers/TelephonyProvider TelephonyProvider.apk
/packages/providers/ContactsProvider ContactsProvider.apk
Service /packages/services/Telecomm Telecomm.apk
/packages/services/Telephony TeleService.apk
/packages/services/Mms MmsService.apk
vendor/mediatek/proprietary/packages/services/Ims ImsService.apk
Framework frameworks/base/telecomm framework.jar
frameworks/base/telephony framework.jar
frameworks/opt/telephony framework.jar
frameworks/opt/net/ims ims-common.jar
vendor/mediatek/proprietary/frameworks/opt/telecomm mediatek-telephony-common.jar
vendor/mediatek/proprietary/frameworks/opt/telephony mediatek-telephony-common.jar
vendor/mediatek/proprietary/frameworks/opt/telephonybase mediatek-telephony-base.jar
HAL vendor/mediatek/proprietary//hardware/ril/fusion librilfusion.so、libmtk-ril.so、mtkfusionrild.bin
vendor/mediatek/proprietary/hardware/c2kril/fusion libvia-ril.so
vendor/mediatek/proprietary/hardware/gsm0710muxd gsm0710muxd.bin
vendor/mediatek/proprietary/external/ccci_mdinit_src ccci_mdinit.bin
vendor/mediatek/proprietary/external/ccci_fsd ccci_fsd.bin
Drivers kernel-4.9/drivers/misc/mediatek/eccci kernel img

1.3、軟件架構

1、功能說明
由於BP的設計都是這樣,於是不管你是ios還是安卓或是塞班還是老人機,通信模塊提供給我們的功能就這麼五個部分:UICC、ServiceState、DataConnect、Call、SMS

功能 簡單介紹
UICC SIM卡:存儲號碼、短信、PIN、PUK、駐網鑑權、STK工具包、2G、3G、4G
ServiceState 網絡服務:網絡制式、運營商名字、信號格數、時區、漫遊、註冊情況
DataConnect 上網服務:2G/3G/4G/5G
Call 通話:撥號、接聽、掛斷、保持、恢復、多方通話
SMS 短信:普通短信、長短信

2、簡單的畫了一張Android通信模塊的架構圖,功能設計完全按照Android分層而來

軟件架構圖中其他幾個功能點簡單介紹

功能 簡單介紹
應用內 Dialer撥號、Contacts聯繫人、Mms短信、settings設置、browser瀏覽器
TeleService Telephony應用框架:數據連接、MMS業務邏輯、Call控制、RILD通信
Telecom 管理通話、和TeleService交互對應用層提供接口
通話 GSMCdmaPhone:2G、3G通話 ImsPhone:4G通話(Volte)
RILD RILD是RILJ和Modem中間層:1、RILJ下發請求->RILD將Request轉換爲Modem的AT指令發送2、Modem上報或者返回的消息->RILD處理傳給RILJ
Gsm0710muxd 1、Gsm0710muxd是AT指令通道進行復用的守護進程2、Gsm0710是開源多路複用協議,提高Modem和AP間AT指令的通信效率
CCCI_FSD 1、Modem不能直接操作文件系統、CCCI_FSD是AP提供給Modem文件守護進程2、通過FSDmodem就可以操作文件系統
CCCI_MDINIT Modem狀態守護進程:啓動、停止、重啓、飛行模式、reset重置
ECCCI driver 驅動框架:複用不同modem驅動、減少中斷內存開支、AT指令轉換modem數據、網卡驅動、文件系統、Audio通話數據

二、RIL詳細介紹

2.1、phone進程

packages\services\Telephony
phone進程是各個功能詳細模塊framework開始的地方
1、自啓動

 <application android:name="PhoneApp"
            android:persistent="true"

在elephony#AndroidManifest.xml中有persistent標記。有此標記AMS會持續保證進程(com.android.phone)存活,意外掛掉也會自動重啓。所以PhoneApp(telephony的Application)就是最初的代碼入口

public class PhoneApp extends Application {
    public void onCreate() {
        mPhoneGlobals = new PhoneGlobals(this);
        mPhoneGlobals.onCreate();
        mTelephonyGlobals = new TelephonyGlobals(this);
        mTelephonyGlobals.onCreate();

2、PhoneGlobals#onCreate
在PhoneGlobals的構造函數裏,開始了Phone對象的創建(makeDefaultPhones).

public void onCreate() {
        PhoneFactory.makeDefaultPhones(this);
    }
public static void makeDefaultPhone(Context context) {
    //有幾張卡創建幾個Phone對象
    sPhones = new Phone[numPhones];
    //創建RILJ對象
    sCommandsInterfaces = new RIL[numPhones];

2.2、RIL

名字 介紹
RILJ java層,運行在telephony的phone進程,它爲上層提供訪問modem的入口和消息接收
RILD HAL層,系統守護進程。RILJ和Modem中間層:1、RILJ下發請求->RILD將Request轉換爲Modem的AT指令發送2、Modem上報或者返回的消息->RILD處理傳給RILJ

在Android8.0之前,RILJ和modem通信時通過socket連接發送AT指令來進行通信。現在的版本已經改用HIDL通信的方式。HIDL和AIDL很類似。可以簡單的理解爲綁定底層的服務來作用到對應的功能上。

2.2.1、RILJ

基本代碼結構

public class RIL extends BaseCommands implements CommandsInterface
public abstract class BaseCommands implements CommandsInterface 
public interface CommandsInterface 

這裏的RIL類就是我們口中的RILJ,採用依賴倒轉原則,定義功能。
<1>、 CommandsInterface接口中定義一些常量表示狀態,再抽象一些方法。
<2>、抽象類實現方法,觀察者模式 register、unregister註冊監聽。留空一些方法具體實現在RIL類中實現
<3>、RIL類則是具體的功能實現。
<4>、在BaseCommands中定義了大量RegistrantList和Registrant對象,這兩個對象是對handler消息的封裝,結合register註冊的監聽,就能把從RILD發送上來的消息傳遞給監聽的對象
<5>、RILJ消息的下發和接收都是通過HIDL拿到radio service,間接通過hidl service來操作和modem之間消息的上傳和下發。
獲得radio hidl service
RIL.java中RIL#getRadioProxy

public IRadio getRadioProxy(Message result) {
        ...
        try {
            mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId],true);   // mPhoneId sim卡id,一張卡一個service
             if (mRadioProxy != null) {
                mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
                        mRadioProxyCookie.incrementAndGet());
                mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication);
            } else {
                riljLoge("getRadioProxy: mRadioProxy == null");
            }
        ...
        return mRadioProxy;
    }

下發

@Override
    public void getIccCardStatus(Message result) {
        IRadio radioProxy = getRadioProxy(result);
        if (radioProxy != null) {
             // 封裝要發送的數據
            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_STATUS, result,
                    mRILDefaultWorkSource);
      
            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

            try {
                // 調用到HIDL 提供的radioservice
                radioProxy.getIccCardStatus(rr.mSerial);
            } catch (RemoteException | RuntimeException e) {
                handleRadioProxyExceptionForRR(rr, "getIccCardStatus", e);
            }
        }
    }

上傳
在獲得hw aodio service的時候,setResponseFunctions(mRadioResponse, mRadioIndication)傳入了兩個對象,他們負責消息的接收和監聽。
這裏隨便複製了一個狀態改變的回調:RadioIndication#radioStateChanged

public void radioStateChanged(int indicationType, int radioState) {
        mRil.processIndication(indicationType);

        CommandsInterface.RadioState newState = getRadioStateFromInt(radioState);
        if (RIL.RILJ_LOGD) {
            mRil.unsljLogMore(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " +
                    newState);
        }

        mRil.setRadioState(newState);
    }

2.2.2 RILD


RILD程序架構圖清晰的表示了RILD的一個初始化過程。歸納爲如下四點
1、ril.cpp來統管功能,具體實現在ril_event,reference_ril,atchanel,ril_service裏
2、第一個流程:startEventLoop開啓loop循環
3、第二個流程:rilInit初始化,打開AT通道流程
4、第三個流程: 註冊回調函數,ril管理

2.2.2.1、loop循環簡要說明

第一個流程開啓loop循環後,按流程圖走到ril_event#ril_event_loop方法,這裏和handler原理中loop循環類似,無限循環查找處理三個鏈表中的event,下面截取最後循環的代碼和鏈表結構體

static struct ril_event * watch_table[MAX_FD_EVENTS];
static struct ril_event timer_list;
static struct ril_event pending_list;

struct ril_event {
    struct ril_event *next;
    struct ril_event *prev;

    int fd;
    int index;
    bool persist;
    struct timeval timeout;
    ril_event_cb func;
    void *param;
};

void ril_event_loop()
{
    for (;;) {
        // Check for timeouts
        processTimeouts();
        // Check for read-ready
        processReadReadies(&rfds, n);
        // Fire away
        firePending();
    }
}

2.2.2.2、打開AT通道簡要說明

打開AT通道,按代碼流程rilInit走到at_open時,主要執行了readerLoop,在readerLoop中,此時AT通道有消息就會被處理。分別由s_unsolHandler 處理主動上報的消息(比如有短消息),processLine經過上層請求過後的需要回復到上層的消息,比如請求sim卡信息

static void *readerLoop(void *arg __unused)
{
    for (;;) {
            line1 = strdup(line);
            line2 = readline();
            if (s_unsolHandler != NULL) {
                s_unsolHandler (line1, line2);
            }
        } else {
            processLine(line);
        }
}

s_unsolHandler上報到RILJ

// ril.cpp
void RIL_onUnsolicitedResponse(int unsolResponse, const void *data,
                                size_t datalen) {
    ret = s_unsolResponses[unsolResponseIndex].responseFunction(
            (int) soc_id, responseType, 0, RIL_E_SUCCESS, const_cast<void*>(data),
            datalen);

static UnsolResponseInfo s_unsolResponses[] = {
#include "ril_unsol_commands.h"
};

//ril_unsol_commands.h
    {RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, radio::radioStateChangedInd, WAKE_PARTIAL},
    {RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, radio::callStateChangedInd, WAKE_PARTIAL},
    {RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, radio::networkStateChangedInd, WAKE_PARTIAL},
    ...

// ril_service.cpp
int radio::radioStateChangedInd(int slotId,
        Return<void> retStatus = radioService[slotId]->mRadioIndication->radioStateChanged(
                convertIntToRadioIndicationType(indicationType), radioState);
        radioService[slotId]->checkReturnStatus(retStatus);
    }

    return 0;
}

sp<RadioImpl> radioService[SIM_COUNT];

struct RadioImpl : public V1_1::IRadio {
    int32_t mSlotId;
    sp<IRadioResponse> mRadioResponse;
    sp<IRadioIndication> mRadioIndication;

<1>、處理先也是流程化傳遞,先是ril.cpp的RIL_onUnsolicitedResponse然後通過結構體傳遞到ril_unsol_commands.h裏聲明的radio::radioStateChangedInd等方法
<2>、radio::radioStateChangedInd等方法的具體實現在ril_service.cpp中
<3>、ril_service在實現的時候,上報就交給了ril_service中RadioImpl結構體裏的mRadioResponse和mRadioIndication。這兩個對象就是2.2.1、RILJ小結獲得hw radio service的時候傳進來的兩個對象。
<4>、總結一下就是rild初始化打開AT通道後,從AT通道中讀到要上報的消息時,交給mRadioResponse和mRadioIndication來上報。RILJ拿到的hw radio service 就是ril_service
<5>、RadioImpl 這個將在第三部分講,可以簡單理解它爲ril_service的代理。
processLine上報
這種上報也有幾種類型

static void processLine(const char *line)
{
 if (sp_response == NULL) {
        /* no command pending */  
        handleUnsolicited(line); //  1、主動上報
    } else if (isFinalResponseSuccess(line)) {
        sp_response->success = 1;
        handleFinalResponse(line); // 2、成功,標準響應
    } else if (isFinalResponseError(line)) {
        sp_response->success = 0;
        handleFinalResponse(line);  // 3、失敗,標準響應
    }  else if (s_smsPDU != NULL && 0 == strcmp(line, "> ")) {
        // See eg. TS 27.005 4.3
        // Commands like AT+CMGS have a "> " prompt
        writeCtrlZ(s_smsPDU);   // 4、收到>符號,發送sms數據再繼續等待響應
        s_smsPDU = NULL;
    }  else switch (s_type) {   // 5、命令有具體的響應信息需要對應分析
        // case中省略了處理方法
        case NO_RESULT:
        case NUMERIC:
        case SINGLELINE:
        case MULTILINE:
        break;

// 這裏以handleFinalResponse舉例
static void handleFinalResponse(const char *line)
{
    // 把消息返回RILJ
    sp_response->finalResponse = strdup(line);
    //s_commandcond脫離阻塞狀態 
    pthread_cond_signal(&s_commandcond);
}

static int at_send_command_full_nolock (const char *command, ATCommandType type,
                    const char *responsePrefix, const char *smspdu,
                    long long timeoutMsec, ATResponse **pp_outResponse)
{
    while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
        if (timeoutMsec != 0) {
            err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);
        } else {
            err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
        }

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

int at_send_command (const char *command, ATResponse **pp_outResponse) {
    err = at_send_command_full (command, NO_RESULT, NULL,
                                    NULL, 0, pp_outResponse);
}

//ril_service.cpp
static void onRequest (int request, void *data, size_t datalen, RIL_Token t) {
            err = at_send_command_numeric("AT+CIMI", &p_response);

<1>、processLine處理也有5種類型,流程上都類似。以handleFinalResponse普通流程舉例
<2>、從handleFinalResponse線程鎖跟蹤到at_send_command_full_nolock,線程解鎖被再次喚醒過後把結果傳給了函數參數*pp_outResponse。
<3>再跟蹤at_send_command_full_nolock方法的調用流程,來自at_send_command 方法。而這個pp_outResponse就是這樣傳過來的。
<4>最後截取了ril_service.cpp中一次包含response的請求代碼片段
<5>所以AT請求下發也是ril_service.cpp中衆多方法間接調用at_send_command傳遞即可。

2.2.2.3、註冊回調函數,ril管理說明

RIL_register (const RIL_RadioFunctions *callbacks) {
    radio::registerService(&s_callbacks, s_commands);
}
//ril_service.cpp
void radio::registerService(RIL_RadioFunctions *callbacks, CommandInfo *commands) {
    s_vendorFunctions = callbacks;
    s_commands = commands;
    for (int i = 0; i < simCount; i++) {
        radioService[i] = new RadioImpl;
}

<1>、註冊函數就簡單了,註冊時把callback傳進來還爲每一張SIM卡創建了一個RadioImpl對象
<2>、radioService這裏就對RIL進行收發管理。

2.2.2.4、RILD小結

<1>、RILJ層通過HIDL拿到reference-ril.c提供的radio service,通過service來下發和上傳回調
<2>ril.cpp通過三大流程調用,來實現rild的功能。
<3>radio service下發是調用atchannel.c中at_send_command來給modem發送at指令
<4>radio service上傳是通過set的兩個回調接口向RILJ傳遞信息。或者通過at_send_command傳遞的response回傳消息。

三、寫在最後

對於Android通信模塊來說,主要學習的是流程跟蹤。從調用棧來講,不管是讀取SIM卡信息or狀態,還是上網、撥號、發短信。都是通過RIL層來轉發AT指令。通過對RIL的瞭解以達到了解整個通信模塊咋個工作的目的。如果要具體的去修改或者維護telephony模塊,還需要對具體功能的流程進行分析掌握,才能遊刃有餘。

read the fucking source code!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章