如果您不是 Android 设备或 Thread 芯片供应商,则不必继续阅读了。
本文档将引导您使用最新的 AOSP 源代码,逐步构建新的基于 Android 的 Thread 边界路由器设备。通过本文档,您将了解:
如果您需要支持,请在 GitHub 中提交问题;如果您有任何疑问,请发起讨论。
概览
Android 线程堆栈基于 Google 在 GitHub 上开源的 OpenThread 和 ot-br-posix
。与 OpenThread 在公开 GitHub 代码库中开发的方式一样,Android 线程堆栈也是在公开的 AOSP 代码库中开发的。所有功能和 bug 修复程序都先提交到 AOSP。这样,供应商无需等待常规 Android 版本,即可开始采用最新的 Thread 版本。
架构
整个 Android 线程堆栈由两个主要组件组成:通用系统分区中的核心线程堆栈,以及供应商分区中的线程 HAL 服务。设备供应商通常只需照顾和构建 HAL 服务。
下面简要介绍了 Android 线程堆栈的运作方式:
- 系统服务器中有一个 Java 线程系统服务,用于管理整个堆栈 - 提供线程系统 API、创建 thread-wpan
隧道接口、将线程网络注册到连接服务,并实现边界路由和广告代理功能。
- 核心 Thread / OpenThread 堆栈托管在名为 ot-daemon
的非特权独立原生进程中。ot-daemon
由 Java 系统服务通过专用 AIDL API 直接管理,并通过 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 开发者 Codelab 中的说明操作
如需首次设置 Android 开发环境,请使用以下 Codelab:https://source.android.com/docs/setup/start。在此 Codelab 结束时,您将能够从源代码构建和运行模拟的 Cuttlefish 设备。
构建线程 HAL 服务
在 Cuttlefish 中试用线程
Cuttlefish 是虚拟 Android 设备。在开始构建自己的 HAL 服务之前,最好先在 Cuttlefish 中尝试使用线程,以了解 HAL 的运作方式。
Cuttlefish 中提供了默认的 Thread HAL 服务,该服务使用模拟的 RCP 实现,可通过 UDP 套接字在模拟的 Thread (802.15.4) 无线电之间收发数据包。
在 Cuttlefish 实例中,系统会预安装“ThreadNetworkDemoApp”。打开该应用,将 Cuttlefish 设备加入默认的 Thread 网络。
我们还提供了 ot-ctl
和 ot-cli-ftd
命令行工具,用于配置 Thread 网络以进行测试。这些工具支持您可能已经熟悉的所有 OpenThread CLI 命令。
您可以通过以下方式 grep 查找 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 线程 HAL 服务使用默认线程 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
与网络共享 Mainline 模块中的线程堆栈类似,Cuttlefish 中的默认线程 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
,则线程 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 规则
默认情况下,线程 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;
整合
现在,您已经完成了添加线程所需的几乎所有代码,最后一步是将线程 HAL APEX 和 sepolicy 规则添加到设备的映像中。
为此,您可以将以下代码添加到设备的 Makefile
(例如 device.mk
)中:
PRODUCT_PACKAGES += com.banana.hardware.threadnetwork BOARD_SEPOLICY_DIRS += device/banana/orange/threadnetwork/sepolicy
如果一切正常,您现在应该会看到类似于以下内容的线程 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
自定义
线程主线程模块(实际上是“网络共享”模块的一部分)提供了一些可叠加的配置,供应商可以指定这些配置以自定义堆栈行为。如需查看完整列表,请参阅 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 边界路由器通告。
如需添加叠加层,您需要为 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 测试可确保线程 HAL 服务在设备上按预期运行。您可以使用以下命令运行测试
atest VtsHalThreadNetworkTargetTest
CTS 测试可确保线程 API 在您的设备上按预期运行。您可以使用以下命令运行测试
atest CtsThreadNetworkTestCases
集成测试可更好地保证线程 Mainline 代码在设备上的运行方式。您可以使用以下命令运行测试
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
),因为此类资源不应包含在面向最终用户的用户 build 中。