最近在公司被老大安排負(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_service 或 com.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ì)上由兩部分組成,RILJ 和 RILC。RILJ 負(fù)責(zé)跟上層(Java)交互,他跟 phone 在同一進(jìn)程。會(huì)將上層的請(qǐng)求通過 RILJ 發(fā)送給 RILC,RILC 在 rild 進(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ī)型,這塊的變化可以參考下文。
對(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 還包含CallTackerPhoneCallManger等眾多邏輯。
繼續(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,而是傾向于把 Dialer 和 InCallUI 做成兩個(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