如果您不是 Android 裝置或 Thread 晶片供應商,可以停止閱讀。
本文件將逐步說明如何使用最新的 AOSP 原始碼,建構新的 Android 版 Thread Border Router 裝置。您可以透過本文件瞭解:
如需支援,請在 GitHub 中回報問題,或在有任何問題時開啟 討論。
總覽
Android 執行緒堆疊是以 OpenThread 和 ot-br-posix
為基礎,這兩者皆由 Google 在 GitHub 上開放原始碼。與 OpenThread 在公開 GitHub 存放區中開發的方式相同,Android 執行緒堆疊是在公開的 AOSP 程式碼庫中開發。所有功能和錯誤修正項目都會先提交至 AOSP。這樣一來,供應商就能開始採用最新的 Thread 版本,而無須等待 Android 的定期發布。
架構
整個 Android 執行緒堆疊由兩個主要元件組成:一般系統分區中的核心執行緒堆疊,以及供應商分區中的執行緒 HAL 服務。裝置供應商通常只需要建構 HAL 服務。
以下簡要說明 Android 執行緒堆疊的運作方式:- 系統伺服器中含有 Java 執行緒系統服務,可管理整個堆疊 - 提供執行緒系統 API、建立 thread-wpan
隧道介面、將執行緒網路註冊至 Connectivity 服務,並實作邊界路由和廣告 Proxy 功能。- 核心執行緒 / OpenThread 堆疊會託管在名為 ot-daemon
的非特權獨立原生程序中。ot-daemon
會透過私人 AIDL API 由 Java 系統服務直接管理,並透過 Thread HAL API 存取 Thread 硬體無線電。- 供應商提供的 Thread HAL 服務務必實作 Thread HAL API。通常會以 RCP 的形式運作,並實作 spinel 通訊協定。
程式碼在哪裡?
- Android 執行緒架構 / API 和服務:https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/thread/
- Thread HAL API 和預設服務實作:https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/threadnetwork/
- 匯入的 OpenThread 存放區:https://cs.android.com/android/platform/superproject/main/+/main:external/openthread/
- 已匯入的 ot-br-posix 存放區:https://cs.android.com/android/platform/superproject/main/+/main:external/ot-br-posix/
設定開發環境
如果 Android 裝置供應商已為裝置建立 Android 開發環境,可以略過本節。
如果您是 Android 生態系統的新手,或是想讓 Thread 晶片與 Android 相容,並為裝置供應商提供支援的晶片供應商,請繼續閱讀。
遵循 Android 開發人員程式碼研究室
如要首次設定 Android 開發環境,請使用以下程式碼研究室:https://source.android.com/docs/setup/start。完成本程式碼研究室後,您將能夠從原始碼建構及執行模擬的 Cuttlefish 裝置。
建構 Thread HAL 服務
試用 Cuttlefish 中的 Thread
Cuttlefish 是虛擬 Android 裝置。開始建構自己的 HAL 服務前,建議您先試試 Cuttlefish 中的 Thread,瞭解 HAL 的運作方式。
Cuttlefish 提供預設的 Thread HAL 服務,並透過模擬 RCP 實作,可透過 UDP 通訊端點來回傳輸封包,並模擬 Thread (802.15.4) 無線電。
在 Cuttlefish 例項中,系統會預先安裝「ThreadNetworkDemoApp」。開啟該應用程式,將 Cuttlefish 裝置加入預設的 Thread 網路。
您也可以使用 ot-ctl
和 ot-cli-ftd
指令列工具設定 Thread 網路以進行測試。這些工具支援您可能已熟悉的所有 OpenThread 指令列指令。
您可以透過以下方式搜尋 Cuttlefish Thread HAL 服務的記錄:
adb logcat | egrep -i threadnetwork-service
07-21 10:43:05.048 0 0 I init : Parsing file /apex/com.android.hardware.threadnetwork/etc/threadnetwork-service.rc...
07-21 10:59:27.233 580 580 W android.hardware.threadnetwork-service: ThreadChip binder is unlinked
07-21 10:59:27.233 580 580 I android.hardware.threadnetwork-service: Close IThreadChip successfully
07-21 10:59:27.385 580 580 I android.hardware.threadnetwork-service: Open IThreadChip successfully
或者,您也可以使用 grep 搜尋 ot-daemon 記錄檔,方法如下:
adb logcat | egrep -i ot-daemon
07-21 10:43:48.741 0 0 I init : starting service 'ot-daemon'...
07-21 10:43:48.742 0 0 I init : Created socket '/dev/socket/ot-daemon/thread-wpan.sock', mode 660, user 1084, group 1084
07-21 10:43:48.762 0 0 I init : ... started service 'ot-daemon' has pid 2473
07-21 10:46:26.320 2473 2473 I ot-daemon: [I] P-Daemon------: Session socket is ready
07-21 10:46:30.290 2473 2473 W ot-daemon: [W] P-Daemon------: Daemon read: Connection reset by peer
07-21 10:48:07.264 2473 2473 I ot-daemon: [INFO]-BINDER--: Start joining...
07-21 10:48:07.267 2473 2473 I ot-daemon: [I] Settings------: Saved ActiveDataset
07-21 10:48:07.267 2473 2473 I ot-daemon: [I] DatasetManager: Active dataset set
07-21 10:48:07.273 2473 2473 I ot-daemon: [I] DnssdServer---: Started
07-21 10:48:07.273 2473 2473 I ot-daemon: [N] Mle-----------: Role disabled -> detached
07-21 10:48:07.273 2473 2473 I ot-daemon: [I] Mle-----------: AttachState Idle -> Start
07-21 10:48:07.273 2473 2473 I ot-daemon: [I] Notifier------: StateChanged (0x111fd11d) [Ip6+ Role LLAddr MLAddr KeySeqCntr Ip6Mult+ Channel PanId NetName ExtPanId ...
07-21 10:48:07.273 2473 2473 I ot-daemon: [I] Notifier------: StateChanged (0x111fd11d) ... NetworkKey PSKc SecPolicy NetifState ActDset]
Cuttlefish Thread HAL 服務會使用預設的 Thread HAL 服務,以及 OpenThread 模擬的 RCP 二進位檔,請參閱下一節瞭解其運作方式。
預設 HAL 服務
除了 Thread HAL API,系統還會提供預設 HAL 服務。預設的 HAL 服務支援模擬和實際的 RCP 裝置。它會接收選用的 RCP 裝置網址,如果未提供網址,則預設為模擬的 RCP 裝置。
在檔案 hardware/interfaces/threadnetwork/aidl/default/threadnetwork-service.rc
中:
service vendor.threadnetwork_hal /apex/com.android.hardware.threadnetwork/bin/hw/android.hardware.threadnetwork-service class hal user thread_network
等同於:
service vendor.threadnetwork_hal /apex/com.android.hardware.threadnetwork/bin/hw/android.hardware.threadnetwork-service spinel+hdlc+forkpty:///apex/com.android.hardware.threadnetwork/bin/ot-rcp?forkpty-arg=1 class hal user thread_network
對於實際的 RCP 裝置,它同時支援 SPI 和 UART 介面,您可以分別使用結構定義 spinel+spi://
、spinel+hdlc+uart://
和 spinel+socket://
指定裝置。
瞭解供應商 APEX
與 Tethering 主線模組中的 Thread 堆疊類似,Cuttlefish 中的預設 Thread HAL 服務也會封裝在 APEX 模組中。但這是供應商 APEX 模組,會安裝至 /vendor/apex/
(模組中的構件會解壓縮至 /apex/com.android.hardware.threadnetwork/
)。
apex { name: "com.android.hardware.threadnetwork", manifest: "manifest.json", file_contexts: "file_contexts", key: "com.android.hardware.key", certificate: ":com.android.hardware.certificate", updatable: false, vendor: true, binaries: [ "android.hardware.threadnetwork-service", "ot-rcp", ], prebuilts: [ "threadnetwork-default.xml", // vintf_fragment "threadnetwork-service.rc", // init_rc "android.hardware.thread_network.prebuilt.xml", // permission ], }
建構自己的 HAL APEX 模組時,您需要留意或變更幾項重要設定:
file_contexts
:這會說明這個 APEX 模組中提供的二進位檔 / 資料檔案,或是 HAL 服務需要存取的檔案 (例如 RCP 裝置)。這樣一來,您就能為 HAL 服務指定特定的 sepolicy 規則,以便存取硬體 RCP 裝置。binaries
:這個 APEX 模組中提供的二進位檔案threadnetwork-service.rc
:如何啟動 HAL 服務。您必須在這裡指定 RCP 裝置路徑。android.hardware.thread_network.prebuilt.xml
:定義android.hardware.thread_network
硬體功能。這項資訊是 Android 系統用來判斷裝置是否支援 Thread 硬體。否則,Android 執行緒堆疊將不會啟用。
建立 HAL 服務
無論您是 Android 裝置開發人員或晶片供應商,都應熟悉為 Thread 晶片建構 OT RCP 韌體。以下操作說明假設硬體晶片已正確連接及驗證。
建構 HAL APEX 最簡單的方法,就是使用預設 HAL APEX 的二進位檔和預先建構檔建立新的 APEX。舉例來說,如果貴公司是 Banana,裝置上的 RCP 裝置是 /dev/ttyACM0
,則 Thread HAL APEX 會如下所示:
Android.bp
:
prebuilt_etc { name: "banana-threadnetwork-service.rc", src: "banana-threadnetwork-service.rc", installable: false, } apex { name: "com.banana.android.hardware.threadnetwork", manifest: "manifest.json", file_contexts: "file_contexts", key: "com.android.hardware.key", certificate: ":com.android.hardware.certificate", updatable: false, vendor: true, binaries: [ "android.hardware.threadnetwork-service", ], prebuilts: [ "banana-threadnetwork-service.rc", "threadnetwork-default.xml", "android.hardware.thread_network.prebuilt.xml", ], }
file_contexts
:
(/.*)? u:object_r:vendor_file:s0 /etc(/.*)? u:object_r:vendor_configs_file:s0 /bin/hw/android\.hardware\.threadnetwork-service u:object_r:hal_threadnetwork_default_exec:s0 /dev/ttyACM0 u:object_r:threadnetwork_rcp_device:s0
第一欄中的檔案路徑與 /apex/com.android.hardware.threadnetwork/
相關。
threadnetwork-service.rc
:
service vendor.threadnetwork_hal /apex/com.android.hardware.threadnetwork/bin/hw/android.hardware.threadnetwork-service spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=115200 class hal user root
manifest.json
:
{ "name": "com.android.hardware.threadnetwork", "version": 1 }
假設您正在製作名為 Orange 的新裝置,則裝置專屬設定目錄會如下所示:
device/banana/orange/threadnetwork/ sepolicy/ Android.bp file_contexts manifest.json threadnetwork-default.xml threadnetwork-service.rc
請參閱下一節,瞭解應在 sepolicy/
子目錄中新增哪些 sepolicy 規則。
RCP 裝置的 Sepolicy 規則
根據預設,Thread HAL 服務無法存取 RCP 裝置 (例如 /dev/ttyACM0
),因此必須將自訂 sepolicy 規則新增至 sepolicy/
目錄。
建立新的 sepolicy/threadnetwork_hal.te
檔案,並加入以下內容:
type threadnetwork_rcp_device, dev_type; # Allows the Thread HAL service to read / write the Thread RCP device allow hal_threadnetwork_default threadnetwork_rcp_device:chr_file rw_file_perms;
整合
您現在已完成新增 Thread 所需的程式碼,最後一步是將 Thread HAL APEX 和 sepolicy 規則新增至裝置的映像檔。
方法是在裝置的 Makefile
(例如 device.mk
) 中新增以下程式碼:
PRODUCT_PACKAGES += com.banana.hardware.threadnetwork BOARD_SEPOLICY_DIRS += device/banana/orange/threadnetwork/sepolicy
如果一切正常,您現在應該會看到類似以下的 Thread HAL 服務記錄:
adb logcat | egrep -i threadnetwork-service
08-13 13:26:41.751 477 477 I android.hardware.threadnetwork-service: ServiceName: android.hardware.threadnetwork.IThreadChip/chip0, Url: spinel+spi
08-13 13:26:41.751 477 477 I android.hardware.threadnetwork-service: Thread Network HAL is running
08-13 13:26:55.165 477 477 I android.hardware.threadnetwork-service: Open IThreadChip successfully
ot-daemon
記錄會如下所示:
adb logcat -s ot-daemon
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Running OTBR_AGENT/Unknown
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Thread version: 1.3.0
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Thread interface: thread-wpan
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Backbone interface is not specified
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Radio URL: threadnetwork_hal://binder?none
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-ILS-----: Infra link selected:
08-13 13:26:55.160 1019 1019 I ot-daemon: [I] Platform------: [HAL] Wait for getting the service android.hardware.threadnetwork.IThreadChip/chip0 ...
08-13 13:26:55.165 1019 1019 I ot-daemon: [I] Platform------: [HAL] Successfully got the service android.hardware.threadnetwork.IThreadChip/chip0
08-13 13:26:55.275 1019 1019 I ot-daemon: [I] P-RadioSpinel-: RCP reset: RESET_UNKNOWN
08-13 13:26:55.276 1019 1019 I ot-daemon: [I] P-RadioSpinel-: Software reset RCP successfully
08-13 13:26:55.277 1019 1019 I ot-daemon: [I] P-RadioSpinel-: RCP reset: RESET_POWER_ON
08-13 13:26:55.322 1019 1019 I ot-daemon: [I] ChildSupervsn-: Timeout: 0 -> 190
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] RoutingManager: Initializing - InfraIfIndex:0
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] InfraIf-------: Init infra netif 0
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] Settings------: Read BrUlaPrefix fd7b:cc45:ff06::/48
08-13 13:26:55.324 1019 1019 I ot-daemon: [N] RoutingManager: BR ULA prefix: fd7b:cc45:ff06::/48 (loaded)
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] RoutingManager: Generated local OMR prefix: fd7b:cc45:ff06:1::/64
08-13 13:26:55.324 1019 1019 I ot-daemon: [N] RoutingManager: Local on-link prefix: fdde:ad00:beef:cafe::/64
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] RoutingManager: Enabling
自訂
執行緒主線程模組 (實際上是「Tethering」模組的一部分) 提供幾個可重疊設定,供應商可指定這些設定來自訂堆疊行為。如需完整清單,請參閱 config_thread.xml。
通常,您必須將 config_thread_border_router_default_enabled
設為 true
,才能讓裝置成為 Thread 邊界路由器,並將 config_thread_vendor_name
、config_thread_vendor_oui
和 config_thread_model_name
變更為供應商或產品值。這些值會納入 _meshcop._udp
mDNS 服務,該服務一律由 Thread Border Router 宣傳。
如要新增疊加層,您必須為 Orange 裝置建立新的 ConnectivityOverlayOrange
runtime_resource_overlay 目標。在 device/banana/orange/rro_overlays
下建立新的 ConnectivityOverlay/
目錄,並在其中建立以下內容:
device/banana/orange/rro_overlays/ConnectivityOverlay/ res values config_thread.xml Android.bp AndroidManifest.xml
Android.bp
:
package { default_applicable_licenses: ["Android-Apache-2.0"], } runtime_resource_overlay { name: "ConnectivityOverlayOrange", manifest: "AndroidManifest.xml", resource_dirs: ["res"], certificate: "platform", product_specific: true, sdk_version: "current", }
AndroidManifest.xml
:
<!-- Orange overlays for the Connectivity module --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.banana.android.connectivity.resources.orange" android:versionCode="1" android:versionName="1.0"> <application android:hasCode="false" /> <!-- If your device uses google-signed mainline modules, the targetPackage needs to be "com.google.android.connectivity.resources", otherise, it should be "com.android.connectivity.resources" --> <overlay android:targetPackage="com.google.android.connectivity.resources" android:targetName="ServiceConnectivityResourcesConfig" android:isStatic="true" android:priority="1"/> </manifest>
config_thread.xml
:
<bool name="config_thread_border_router_default_enabled">true</bool> <string translatable="false" name="config_thread_vendor_name">Banana Inc.</string> <string translatable="false" name="config_thread_vendor_oui">AC:DE:48</string> <string translatable="false" name="config_thread_model_name">Orange</string>
與 HAL APEX 類似,您需要將疊加應用程式新增至 device.mk
檔案:
PRODUCT_PACKAGES += \ ConnectivityOverlayOrange</code>
如果一切正常運作,您會看到 ot-daemon
在記錄的開頭記錄供應商和型號名稱:
adb logcat -s ot-daemon
07-22 15:31:37.693 1472 1472 I ot-daemon: [I] P-Daemon------: Session socket is ready
07-22 15:31:37.693 1472 1472 I ot-daemon: [I] Cli-----------: Input: state
07-22 15:31:37.693 1472 1472 I ot-daemon: [I] Cli-----------: Output: disabled
07-22 15:31:37.693 1472 1472 I ot-daemon: [I] Cli-----------: Output: Done
07-22 15:31:37.693 1472 1472 W ot-daemon: [W] P-Daemon------: Daemon read: Connection reset by peer
07-22 15:31:50.091 1472 1472 I ot-daemon: [I] P-Daemon------: Session socket is ready
07-22 15:31:50.091 1472 1472 I ot-daemon: [I] Cli-----------: Input: factoryreset
07-22 15:31:50.092 1472 1472 I ot-daemon: [I] Settings------: Wiped all info
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-ADPROXY-: Stopped
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-DPROXY--: Stopped
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-BA------: Stop Thread Border Agent
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-BA------: Unpublish meshcop service Banana Inc. Orange #4833._meshcop._udp.local
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-MDNS----: Removing service Banana Inc. Orange #4833._meshcop._udp
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-MDNS----: Unpublishing service Banana Inc. Orange #4833._meshcop._udp listener ID = 0
與 Google Home 相容
此外,如果您想讓邊界路由器供 Google Home 生態系統使用,可以在 config_thread.xml
中指定這項設定:
<string-array name="config_thread_mdns_vendor_specific_txts"> <item>vgh=1</item> </string-array>
測試
你的裝置現在應可與 Thread 1.3 以上版本的邊界路由器規格相容。在將應用程式提交至 Thread 認證計畫之前,您應該執行幾項 Android xTS 測試,確保相容性。
VTS 測試可確保 Thread HAL 服務在裝置上正常運作。您可以使用下列指令執行測試:
atest VtsHalThreadNetworkTargetTest
CTS 測試可確保 Thread API 在裝置上正常運作。您可以使用下列指令執行測試:
atest CtsThreadNetworkTestCases
整合測試可進一步保證 Thread 主線程程式碼在裝置上的運作方式。您可以使用下列指令執行測試:
atest ThreadNetworkIntegrationTests
您也可以參閱更多操作說明,瞭解如何使用這些已發布的測試套件執行 VTS/CTS/MTS 測試:
- https://source.android.com/docs/core/tests/vts
- https://source.android.com/docs/compatibility/cts/run
- https://docs.partner.android.com/mainline/test/mts (您必須是合作夥伴才能存取此連結)
使用 Thread 示範應用程式進行測試
與 Cuttlefish 裝置類似,您可以將 Thread 示範應用程式新增至系統映像檔:
# ThreadNetworkDemoApp for testing PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp
請注意,您應該只將此值新增至偵錯 / eng 變數 (例如 PRODUCT_PACKAGES_DEBUG
),因為這不應納入供最終消費者使用的使用者版本。