Android Telephony 模塊結(jié)構(gòu)簡析

最近在公司被老大安排負(fù)責(zé)跟進(jìn)通訊相關(guān)的一些問題,于是簡單了解一下 Android 通訊相關(guān)的模塊。

說實(shí)話,剛開始看的時(shí)候確實(shí)是一頭霧水,因?yàn)樵O(shè)計(jì)的模塊實(shí)在是太!多!了!要說多都還好,要命的是這些不同的模塊還散落在 AOSP 的不同目錄,如果不梳理記錄一下,跳來跳去怎么都一頭霧水,因此就有了這篇文章,我自己也好趁機(jī)梳理一下模塊直接的聯(lián)系。

本文基于 Android 12 源碼,根據(jù)我自己的理解整理而來的,如果有錯(cuò)誤,歡迎評(píng)論區(qū)指出,畢竟這塊我是新手,感謝。

主要結(jié)構(gòu)

Android telephony 是一個(gè)典型的分層結(jié)構(gòu),其涉及的相關(guān)模塊按照功能,從下到上,可大概分為四類:

HAL 相關(guān):

目錄 產(chǎn)物 運(yùn)行進(jìn)程 主要作用
hardware/ril N/A N/A N/A

Service 相關(guān)

目錄 產(chǎn)物 運(yùn)行進(jìn)程 主要作用
packages/service/telecomm Telecom.apk system_service 橋梁,連接下層(如 telephony)與上層(如 Dailer
packages/service/telephony TeleService.apk com.android.phone 負(fù)責(zé)調(diào)用 frameworks/opt/telephony 邏輯實(shí)現(xiàn)
frameworks/base/telecomm framework.jar N/A 無進(jìn)程,僅暴露框架,從而對(duì)上層提供接口
frameworks/base/telephony framework.jar N/A 無進(jìn)程,僅暴露框架,從而對(duì)上層提供接口
frameworks/opt/telephony telephony-common.jar system_servicecom.android.phone 包含 RILJ 代碼和其它,被 frameworks/opt/telephony 依賴

App 相關(guān)

目錄 產(chǎn)物 運(yùn)行進(jìn)程 主要作用
packages/apps/Dialer Dialer.apk com.android.dialer 撥號(hào)應(yīng)用,包含撥號(hào)盤、通話記錄、暗碼(*#*#4636#*#*這種)、來去電界面(InCallUI)、語音信箱等

Provider 相關(guān)

目錄 產(chǎn)物 包名 主要作用
packages/providers/ContactsProvider ContactsProvider.apk com.android.providers.contacts 對(duì)外提供聯(lián)系人信息相關(guān)的增刪改查
packages/providers/TelephonyProvider TelephonyProvider.apk com.android.providers.telephony 對(duì)外提供通訊功能(比如運(yùn)營商信息配置信息)相關(guān)的增刪改查

結(jié)構(gòu)詳解

RIL

先從底層的 RIL 說起。這里說底層其實(shí)也是相對(duì)而言,因?yàn)?RIL 的全稱是 Radio Interface Layer,既然是個(gè) Layer,也就意味著一定還有上下層。 比 RIL 更底層的就要到 Linux 內(nèi)核 和 Modem 了,咱也不懂,咱也不敢說,因此還是只能冒上水面,淺淺地講一講 RIL。

RIL 本質(zhì)上由兩部分組成,RILJRILC。RILJ 負(fù)責(zé)跟上層(Java)交互,他跟 phone 在同一進(jìn)程。會(huì)將上層的請(qǐng)求通過 RILJ 發(fā)送給 RILCRILCrild 進(jìn)程中,向上負(fù)責(zé)跟 RILJ 進(jìn)行對(duì)接(C++),向下對(duì)接 Modem。

rild (RIL Daemon)是系統(tǒng)的守護(hù)進(jìn)程,系統(tǒng)一啟動(dòng)就會(huì)一直運(yùn)行。手機(jī)開機(jī)時(shí),kernel 完成初始化,系統(tǒng)會(huì)啟動(dòng)一個(gè)初始化進(jìn)程 Init 用于加載系統(tǒng)基礎(chǔ)服務(wù)、Zygote 進(jìn)程、system_server,還有就是 rild

android.googlesource.com/platform/ha…

service ril-daemon /system/bin/rild
    class main
    socket rild stream 660 root radio
    socket sap_uim_socket1 stream 660 bluetooth bluetooth
    socket rild-debug stream 660 radio system
    user root
    group radio cache inet misc audio log readproc wakelock

在這里提一個(gè)對(duì)上層應(yīng)用開發(fā)小伙伴而言相對(duì)偏“冷”的知識(shí)吧。你們肯定聽過一道很經(jīng)典的面試八股題:《Binder 這么好用,那為什么 Zygote 的 IPC 通信機(jī)制用 Socket 而不用 Binder》?背答案的時(shí)候都會(huì)背,實(shí)際用在了哪里呢?在 Android 8.0 之前,RILC 與 Modem 的通訊一直都是采用 Socket,之后 Google 推出了 HIDL,用來簡化代碼,使條理邏輯更加清晰,也方便 OEM 快速適配機(jī)型,這塊的變化可以參考下文。

blog.csdn.net/dxpqxb/arti…

對(duì) RIL 的介紹打算言盡于此,多的我暫時(shí)也還沒研究透。這部分的代碼一般都是由 SOC 廠商和 Google 去維護(hù)的,除非有特殊的通訊相關(guān)需求,否則一般 OEM 也不會(huì)動(dòng)到。RILC 的代碼位于源碼根目錄下 hardware/ril, RILJ 的代碼則位于源碼根目錄下 frameworks/opt/telephony,將在下面提到。

TeleService

這部分的介紹也盡量由下往上,既然上面提到了 RILJ,就先看這部分的代碼。

RILJ 的代碼主要位于 frameworks/opt/telephony,大概看一下這個(gè)項(xiàng)目的 Android.bp

java_library {
    name: "telephony-common",
    installable: true,

    aidl: {
        local_include_dirs: ["src/java"],
    },
    srcs: [
        ":opt-telephony-common-srcs",
        ":framework-telephony-common-shared-srcs",
        ":net-utils-telephony-common-srcs",
        ":statslog-telephony-java-gen",
        ":statslog-cellbroadcast-java-gen",
        "src/java/**/I*.aidl",
        "src/java/**/*.logtags",
    ],

    jarjar_rules: ":jarjar-rules-shared",

    libs: [
        "android.hardware.radio-V1.0-java",
        "android.hardware.radio-V1.1-java",
        "android.hardware.radio-V1.2-java",
        "android.hardware.radio-V1.3-java",
        "android.hardware.radio-V1.4-java",
        "android.hardware.radio-V1.5-java",
        "voip-common",
        "ims-common",
        "unsupportedappusage",
    ],
    static_libs: [
        "android.hardware.radio.config-V1.0-java-shallow",
        "android.hardware.radio.config-V1.1-java-shallow",
        "android.hardware.radio.config-V1.2-java-shallow",
        "android.hardware.radio.deprecated-V1.0-java-shallow",
        "ecc-protos-lite",
        "libphonenumber-nogeocoder",
        "PlatformProperties",
        "net-utils-framework-common",
        "telephony-protos",
    ],

}

可以得出如下信息:

  • 項(xiàng)目代碼包含 java ,還定義了一些 aidl 接口,并且用了 HIDL 和底層交互
  • 項(xiàng)目產(chǎn)物是 telephony-common.jar
  • 可以根據(jù)分包名稱大概知道都包含哪些功能接口。

另外通過閱讀項(xiàng)目下的 README.txt 還可以得知,telephony-common.jar 里很多 API 都是通過 HIDL 調(diào)用定義在 hardware/interfaces/radio/ hardware/interfaces/radio/config 的接口的方式下放給 radio 層來獲得返回值的。另外,在 frameworks/base/telephony/ 里有一些 aidl 定義,在這個(gè)庫和 packages/services/Telephony 這兩個(gè)庫里實(shí)現(xiàn),這套 IPC 機(jī)制保證了調(diào)用者可以在它們自己的進(jìn)程里跑公開的 API 代碼,而和通訊相關(guān)的代碼則跑在 com.android.phone,類似的實(shí)現(xiàn)可以參考 PhoneInterfaceManager, SubscriptionController

注意,雖然 RILJ 的代碼位于 Telephony,但不要認(rèn)為 Telephony 整個(gè)就是為了實(shí)現(xiàn) RILJ,因?yàn)槌酥猓?code>RILJ 還包含 CallTacker Phone CallManger 等眾多邏輯。

繼續(xù)我們的分析,既然上文提到了,于是我們查看一下 packages/services/Telephony,打開它的 Android.bp


// Build the Phone app which includes the emergency dialer. See Contacts
// for the 'other' dialer.

android_app {
    name: "TeleService",

    libs: [
        "telephony-common",
        "voip-common",
        "ims-common",
        "libprotobuf-java-lite",
        "unsupportedappusage",
    ],

    static_libs: [
        "androidx.appcompat_appcompat",
        "androidx.preference_preference",
        "androidx.recyclerview_recyclerview",
        "androidx.legacy_legacy-preference-v14",
        "android-support-annotations",
        "com.android.phone.common-lib",
        "guava",
        "PlatformProperties",
    ],

    srcs: [
        ":framework-telephony-common-shared-srcs",
        "src/**/*.java",
        "sip/src/**/*.java",
        "ecc/proto/**/*.proto",
        "src/com/android/phone/EventLogTags.logtags",
    ],

    jarjar_rules: ":jarjar-rules-shared",

    resource_dirs: [
        "res",
        "sip/res",
    ],

    asset_dirs: [
        "assets",
        "ecc/output",
    ],

    aaptflags: [
        "--extra-packages com.android.services.telephony.sip",
    ],

    platform_apis: true,

    certificate: "platform",
    privileged: true,

    optimize: {
        proguard_flags_files: [
            "proguard.flags",
            "sip/proguard.flags",
        ],
    },

    proto: {
        type: "lite",
    },
}

可以得出如下信息:

  • 項(xiàng)目的確依賴了 telephony-common,除此之外還有諸如 voip-common, ims-common 等模塊
  • 產(chǎn)物是 TeleService.apk
  • TeleService.apk 內(nèi)部還包含了緊急通話的一些邏輯,包括緊急通話的撥號(hào)盤,而常規(guī)模式下的撥號(hào)盤,在 Dialer 模塊

既然是 apk,我們有必要看一下它的 AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        package="com.android.phone"
        coreApp="true"
        android:sharedUserId="android.uid.phone"
        android:sharedUserLabel="@string/phoneAppLabel">

        <application android:name="PhoneApp"
            android:persistent="true"
            android:label="@string/phoneAppLabel"
            android:icon="@mipmap/ic_launcher_phone"
            android:allowBackup="false"
            android:supportsRtl="true"
            android:usesCleartextTraffic="true"
            android:defaultToDeviceProtectedStorage="true"
            android:directBootAware="true">

            ...

    </application>
</manifest>

這下子我們又可以知道更多信息:

  • PhoneApp.java 是整個(gè) App 的入口,在 Application 里應(yīng)該有初始化的邏輯。
  • TeleService.apk 會(huì)運(yùn)行在 com.android.phone 進(jìn)程
  • coreApp="true"聲明了這是個(gè)重要的系統(tǒng)模塊,android:directBootAware="true" 保證了它在開機(jī)后即使用戶沒解鎖也可以立刻啟動(dòng)

再簡單看一下代碼結(jié)構(gòu),主要包含兩個(gè)包:

  • com.android.phone:基礎(chǔ)包,包含 PhoneApp、PhoneGlobals 等很多基礎(chǔ)類。另外,撥號(hào)盤->設(shè)置->更多設(shè)置頁面和邏輯也在這里實(shí)現(xiàn)。
  • com.android.services.telephony:與通訊相關(guān)的服務(wù)的具體實(shí)現(xiàn)代碼,比如 TelephonyConnectionService

Telecom

現(xiàn)在有了 service,又有了應(yīng)用,他們之間是不是就可以直接通訊了呢?答案是否定的,在他們之間還有一個(gè)承上啟下的關(guān)鍵組件 Telecom.apk,位于源碼根目錄的 packages/services/Telecomm,為了研究它,我們照理從它的 Android.bp入手:

genrule {
    /.../
}

filegroup {
    name: "Telecom-srcs",
    srcs: [
        "src/**/*.java",
        ":statslog-telecom-java-gen",
    ],
}

// Build the Telecom service.
android_app {   
    name: "Telecom",
    srcs: [
        ":Telecom-srcs",
        "proto/**/*.proto",
    ],
    resource_dirs: ["res"],
    proto: {
        type: "nano",
        local_include_dirs: ["proto/"],
        output_params: ["optional_field_style=accessors"],
    },
    platform_apis: true,
    certificate: "platform",
    privileged: true,
    optimize: {
        proguard_flags_files: ["proguard.flags"],
    },
    defaults: ["SettingsLibDefaults"],
}

android_test {
    /.../
}

可以看出:

  • Telecom 是一個(gè) service,產(chǎn)物是 Telecom.apk。
  • 從代碼目錄的 callfiltering、callredirection 分包可以窺見,Telecom 應(yīng)該在上層應(yīng)用(如 Dialer.apk)和下層服務(wù)(比 TeleService.apk) 之間,起到一個(gè)承上啟下的左右,由它來過濾和溝通所有的請(qǐng)求是否要下方到底層服務(wù),并在接收到底層服務(wù)后,再經(jīng)由它傳遞給上層應(yīng)用??上攵?,這個(gè)結(jié)構(gòu)的好處使得不管是上層應(yīng)用,還是底層服務(wù),都只要專注做好自己領(lǐng)域的工作,而其它過濾、日志、跟蹤記錄等行為,就交由它這個(gè)中間層來執(zhí)行。
  • 有同學(xué)可能會(huì)想就此感慨一下 Android 架構(gòu)設(shè)計(jì)的巧妙之處,其實(shí) Telecom 也是 Android 5.0 之后才被引入的,Google 在 packages/services/Telecomm/src/com/android/server/telecom/README 有寫到,大部分代碼都是從 TeleService.apk 模塊剝離到這里的。所以好的架構(gòu)從來都不是天生的,也需要后期演變。

照例,既然是 apk,我們需要大概看一下 AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        package="com.android.server.telecom"
        coreApp="true"
        android:sharedUserId="android.uid.system">

    <protected-broadcast android:name="android.intent.action.SHOW_MISSED_CALLS_NOTIFICATION" />
    <protected-broadcast android:name="com.android.server.telecom.MESSAGE_SENT" />

     <application android:label="@string/telecommAppLabel"
            android:icon="@mipmap/ic_launcher_phone"
            android:allowBackup="false"
            android:supportsRtl="true"
            android:process="system"
            android:usesCleartextTraffic="false"
            android:defaultToDeviceProtectedStorage="true"
            android:directBootAware="true">

    </application>
</manifest>

可以得知:

  • Telecom 的進(jìn)程應(yīng)該是 system_server,使用平臺(tái)簽名
  • 模塊沒有像 TeleSevice 那樣在 Application 里去初始化邏輯,它的啟動(dòng)入口在 frameworks/base/services/core/java/com/android/server/telecom/TelecomLoaderService.java ,并在啟動(dòng) system service 階段跟隨一起起來。

Dialer

Dialer 顧名思義就是“撥號(hào)盤”了,代碼位于 packages/apps/Dialer,這個(gè)沒什么好講的。需要注意的是,Dialer 內(nèi)部還包含了通話界面(InCallUI)、語音信箱(voicemail)的代碼。實(shí)際開發(fā)過程中,有些 OEM 廠商并不一定會(huì)內(nèi)置這個(gè) Dialer,而是傾向于把 DialerInCallUI 做成兩個(gè)獨(dú)立的 apk 內(nèi)置,也方便做 SDK 化,跟系統(tǒng)源碼解耦。

如此一來,我們可以對(duì)上面的圖進(jìn)行一次修改,補(bǔ)充一下模塊名稱,并且修改一下各個(gè)模塊的描述:

總結(jié)

以上就是對(duì) Android Telephony 相關(guān)模塊結(jié)構(gòu)的簡單解析。本文不對(duì)模塊做深入,也不進(jìn)行源碼分析,因?yàn)槲易约阂策€在學(xué)習(xí)當(dāng)中。后面等我看得差不多了,有時(shí)間再來針對(duì)每個(gè)模塊寫一些源碼解析的文章。

作者:Mr_萬能膠
鏈接:https://juejin.cn/post/7124609389180223496

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容