Quellcode in der Android Code Search ansehen
Wenn Sie kein Android-Gerät oder kein Thread-Chipanbieter sind, können Sie jetzt aufhören zu lesen.
In diesem Dokument erfahren Sie, wie Sie ein neues Android-basiertes Thread-Border-Router-Gerät mit dem neuesten AOSP-Quellcode erstellen. In diesem Dokument erfahren Sie Folgendes:
- die allgemeine Architektur und den Status der Thread-Unterstützung in Android
- how to create your own Thread HAL service
- Informationen dazu, wie Sie Ihr Gerät mit Google Home kompatibel machen
- So testen Sie Ihren Thread-Border-Router
Wenn Sie Support benötigen, erstellen Sie einen Problemfall in GitHub oder eröffnen Sie eine Diskussion, wenn Sie Fragen haben.
Übersicht
Der Android-Thread-Stack basiert auf OpenThread und ot-br-posix
, die von Google auf GitHub als Open Source veröffentlicht wurden. Genauso wie OpenThread in einem öffentlichen GitHub-Repository entwickelt wird, wird der Android-Thread-Stack in der öffentlichen AOSP-Codebasis entwickelt. Alle Funktionen und Fehlerkorrekturen werden zuerst in AOSP eingereicht. So können Anbieter die neuesten Thread-Versionen einführen, ohne auf regelmäßige Android-Releases warten zu müssen.
Architektur
Der gesamte Android-Thread-Stack besteht aus zwei Hauptkomponenten: dem Kern-Thread-Stack in einer generischen Systempartition und dem Thread HAL-Dienst in einer Anbieterpartition. Geräteanbieter müssen in der Regel nur den HAL-Dienst erstellen.
Hier eine kurze Zusammenfassung der Funktionsweise des Android-Thread-Stacks:
– Es gibt einen Java-Thread-Systemdienst auf dem Systemserver, der den gesamten Stack verwaltet. Er stellt die Thread-System-API bereit, erstellt die thread-wpan
-Tunnelschnittstelle, registriert das Thread-Netzwerk beim Konnektivitätsdienst und implementiert die Border-Routing- und Advertising-Proxy-Funktionen.
– Der Thread-/OpenThread-Stack wird in einem nicht privilegierten eigenständigen nativen Prozess namens ot-daemon
gehostet. ot-daemon
wird direkt vom Java-Systemdienst über private AIDL APIs verwaltet und greift über die Thread HAL API auf die Thread-Hardwareschnittstelle zu.
– Ein vom Anbieter bereitgestellter Thread HAL-Dienst MUSS die Thread HAL API implementieren. Sie arbeitet in der Regel als RCP und implementiert das spinel-Protokoll.
Wo finde ich den Code?
- Android-Thread-Framework/-API und -Dienst: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/thread/
- Thread HAL API und Standarddienstimplementierung: https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/threadnetwork/
- Importiertes OpenThread-Repository: https://cs.android.com/android/platform/superproject/main/+/main:external/openthread/
- Importiertes ot-br-posix-Repository: https://cs.android.com/android/platform/superproject/main/+/main:external/ot-br-posix/
Entwicklungsumgebung einrichten
Android-Gerätehersteller, die bereits eine Android-Entwicklungsumgebung für das Gerät eingerichtet haben, können diesen Abschnitt überspringen.
Wenn Sie neu im Android-System sind oder ein Chipanbieter sind, der seinen Thread-Chip mit Android kompatibel machen und Support für Geräteanbieter bereitstellen möchte, lesen Sie weiter.
Codelab für Android-Entwickler
Wenn Sie Ihre Android-Entwicklungsumgebung zum ersten Mal einrichten möchten, verwenden Sie das folgende Codelab: https://source.android.com/docs/setup/start. Am Ende dieses Codelabs können Sie ein simuliertes Cuttlefish-Gerät aus dem Quellcode erstellen und ausführen.
Thread HAL-Dienst erstellen
Thread in Cuttlefish ausprobieren
Cuttlefish ist das virtuelle Android-Gerät. Bevor Sie mit dem Erstellen Ihres eigenen HAL-Dienstes beginnen, sollten Sie Thread in Cuttlefish ausprobieren, um zu verstehen, wie HAL funktioniert.
In Cuttlefish wird ein standardmäßiger Thread HAL-Dienst bereitgestellt, der mit dem simulierten RCP implementiert ist, der Pakete über einen UDP-Socket von und zu einem simulierten Thread-Funkschnittstellen (802.15.4) sendet und empfängt.
In der Cuttlefish-Instanz ist die „ThreadNetworkDemoApp“ vorinstalliert. Öffnen Sie diese App, um das Cuttlefish-Gerät mit einem Standard-Thread-Netzwerk zu verbinden.
Außerdem gibt es die Befehlszeilentools ot-ctl
und ot-cli-ftd
, mit denen Sie Ihr Thread-Netzwerk für den Test konfigurieren können. Diese Tools unterstützen alle OpenThread-Befehle für die Befehlszeile, die Sie möglicherweise bereits kennen.
So können Sie mit Grep nach Logs des Cuttlefish Thread HAL-Dienstes suchen:
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
Oder Sie können mit grep nach ot-Daemon-Logs suchen:
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]
Der Cuttlefish-Thread-HAL-Dienst verwendet den standardmäßigen Thread-HAL-Dienst sowie das simulierte RCP-Binärprogramm von OpenThread. Im nächsten Abschnitt wird die Funktionsweise erläutert.
Der Standard-HAL-Dienst
Neben der Thread HAL API ist auch ein Standard-HAL-Dienst enthalten. Der Standard-HAL-Dienst unterstützt sowohl simulierte als auch echte RCP-Geräte. Es erhält eine optionale RCP-Geräte-URL. Wenn die URL nicht angegeben ist, wird standardmäßig das simulierte RCP-Gerät verwendet.
In der Datei 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
Dies entspricht:
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
Für echte RCP-Geräte werden sowohl SPI- als auch UART-Schnittstellen unterstützt. Sie können das Gerät mit den Schemas spinel+spi://
, spinel+hdlc+uart://
und spinel+socket://
angeben.
APEX des Anbieters
Ähnlich wie der Thread-Stack im Tethering-Mainline-Modul ist auch der standardmäßige Thread-HAL-Dienst in Cuttlefish in einem APEX-Modul verpackt. Es ist jedoch ein APEX-Modul eines Anbieters, das unter /vendor/apex/
installiert wird. Die Artefakte im Modul werden in /apex/com.android.hardware.threadnetwork/
entpackt.
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 ], }
Es gibt einige wichtige Konfigurationen, die Sie beim Erstellen Ihres eigenen HAL-APEX-Moduls beachten oder ändern müssen:
file_contexts
: Hier werden die Binär-/Dateien beschrieben, die in diesem APEX-Modul bereitgestellt werden, oder Dateien, auf die der HAL-Dienst zugreifen muss (z. B. das RCP-Gerät). So können Sie für Ihren HAL-Dienst bestimmte SEPolicy-Regeln für den Zugriff auf das Hardware-RCP-Gerät angeben.binaries
: Die Binärdatei, die in diesem APEX-Modul bereitgestellt wirdthreadnetwork-service.rc
: Wie der HAL-Dienst gestartet wird. Hier müssen Sie den RCP-Gerätepfad angeben.android.hardware.thread_network.prebuilt.xml
: Definiert dieandroid.hardware.thread_network
-Hardwarefunktion. Das ist erforderlich, damit das Android-System weiß, dass Ihr Gerät Thread-Hardware unterstützt. Andernfalls wird der Android-Thread-Stack nicht aktiviert.
HAL-Dienst erstellen
Unabhängig davon, ob Sie ein Android-Geräteentwickler oder ein Chipanbieter sind, sollten Sie mit der Erstellung von OT RCP-Firmware für Ihren Thread-Chip vertraut sein. In der folgenden Anleitung wird davon ausgegangen, dass der Hardwarechip richtig verkabelt und validiert ist.
Am einfachsten erstellen Sie Ihre HAL-APEX, indem Sie eine neue APEX mit den Binärdateien und Prebuilts der Standard-HAL-APEX erstellen. Wenn Ihr Unternehmen beispielsweise „Apfel“ heißt und das RCP-Gerät auf Ihrem Gerät /dev/ttyACM0
ist, sieht Ihre Thread HAL APEX so aus:
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
Die Dateipfade in der ersten Spalte beziehen sich auf /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 }
Angenommen, Sie erstellen ein neues Gerät mit dem Namen „Orange“, sieht das entsprechende Konfigurationsverzeichnis so aus:
device/banana/orange/threadnetwork/ sepolicy/ Android.bp file_contexts manifest.json threadnetwork-default.xml threadnetwork-service.rc
Im nächsten Abschnitt erfahren Sie, welche Sepolicy-Regeln im Unterverzeichnis sepolicy/
hinzugefügt werden sollten.
Sepolicy-Regeln für RCP-Gerät
Standardmäßig hat Ihr Thread HAL-Dienst keinen Zugriff auf das RCP-Gerät (z. B. /dev/ttyACM0
). Dem Verzeichnis sepolicy/
müssen benutzerdefinierte SEPolicy-Regeln hinzugefügt werden.
Erstellen Sie eine neue sepolicy/threadnetwork_hal.te
-Datei mit folgendem Inhalt:
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;
Zusammenstellen
Sie haben jetzt fast den gesamten Code für das Hinzufügen von Thread fertiggestellt. Als letzten Schritt müssen Sie dem Image Ihres Geräts die Thread HAL APEX- und sepolicy-Regeln hinzufügen.
Fügen Sie dazu den folgenden Code in die Makefile
Ihres Geräts ein (z. B. device.mk
):
PRODUCT_PACKAGES += com.banana.hardware.threadnetwork BOARD_SEPOLICY_DIRS += device/banana/orange/threadnetwork/sepolicy
Wenn alles funktioniert, sehen Sie jetzt das Thread HAL-Dienstprotokoll, das in etwa so aussieht:
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
Das ot-daemon
-Protokoll sieht dann so aus:
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
Anpassung
Das Thread-Mainline-Modul (tatsächlich Teil des „Tethering“-Moduls) bietet einige Overlay-Konfigurationen, die von Anbietern angegeben werden können, um das Stack-Verhalten anzupassen. Eine vollständige Liste findest du unter config_thread.xml.
Normalerweise müssen Sie config_thread_border_router_default_enabled
auf true
setzen, um Ihr Gerät als Thread-Border-Router zu aktivieren, und config_thread_vendor_name
, config_thread_vendor_oui
und config_thread_model_name
auf die Werte Ihres Anbieters oder Produkts ändern. Diese Werte werden in den _meshcop._udp
-mDNS-Dienst aufgenommen, der immer von einem Thread-Border-Router beworben wird.
Wenn Sie das Overlay hinzufügen möchten, müssen Sie ein neues Ziel vom Typ ConnectivityOverlayOrange
runtime_resource_overlay für Ihr Orange-Gerät erstellen. Erstellen Sie unter device/banana/orange/rro_overlays
ein neues Verzeichnis ConnectivityOverlay/
und fügen Sie ihm die folgenden Inhalte hinzu:
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>
Ähnlich wie bei HAL APEX müssen Sie die Overlay-App der device.mk
-Datei hinzufügen:
PRODUCT_PACKAGES += \ ConnectivityOverlayOrange</code>
Wenn alles funktioniert, sehen Sie, dass ot-daemon
den Namen des Anbieters und des Modells ganz am Anfang des Logs protokolliert:
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
mit Google Home kompatibel sein
Wenn Sie möchten, dass Ihr Border-Router vom Google Home-System verwendet wird, können Sie diese Konfiguration in config_thread.xml
angeben:
<string-array name="config_thread_mdns_vendor_specific_txts"> <item>vgh=1</item> </string-array>
Test
Ihr Gerät sollte jetzt mit der Border-Router-Spezifikation von Thread 1.3 und höher kompatibel sein. Bevor Sie das Gerät an das Thread-Zertifizierungsprogramm senden, sollten Sie einige Android-xTS-Tests durchführen, um die Kompatibilität zu prüfen.
Der VTS-Test stellt sicher, dass der Thread HAL-Dienst auf Ihrem Gerät wie erwartet funktioniert. Sie können die Tests mit dem Befehl
atest VtsHalThreadNetworkTargetTest
Der CTS-Test stellt sicher, dass Thread-APIs auf Ihrem Gerät wie erwartet funktionieren. Sie können die Tests mit dem Befehl
atest CtsThreadNetworkTestCases
Der Integrationstest bietet eine bessere Qualitätsgarantie dafür, wie der Thread-Mainline-Code auf Ihrem Gerät funktioniert. Sie können die Tests mit dem Befehl
atest ThreadNetworkIntegrationTests
Weitere Anleitungen zum Ausführen von VTS-/CTS-/MTS-Tests mit diesen veröffentlichten Test-Suites:
- https://source.android.com/docs/core/tests/vts
- https://source.android.com/docs/compatibility/cts/run
- https://docs.partner.android.com/mainline/test/mts (Sie müssen Partner sein, um auf diesen Link zugreifen zu können)
Mit der Thread-Demo-App testen
Ähnlich wie beim Cuttlefish-Gerät können Sie Ihrem System-Image die Thread-Demo-App hinzufügen:
# ThreadNetworkDemoApp for testing PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp
Hinweis: Sie sollten sie nur der Debug-/Eng-Variante hinzufügen (z. B. PRODUCT_PACKAGES_DEBUG
), da sie nicht in den Nutzerbuild für Endverbraucher aufgenommen werden soll.