Afficher le code source sur Android Code Search
Si vous n'êtes pas un fournisseur d'appareils Android ou de puces Thread, vous pouvez arrêter de lire cet article.
Ce document vous explique comment créer un nouveau routeur de bord Thread basé sur Android avec le dernier code source AOSP. En suivant ce document, vous allez apprendre:
- l'architecture globale et l'état de la prise en charge de Thread dans Android
- créer votre propre service HAL Thread ;
- rendre votre appareil compatible avec Google Home
- comment tester votre routeur de bordure Thread
Si vous avez besoin d'aide, signalez un problème sur GitHub ou ouvrez une discussion si vous avez des questions.
Présentation
La pile de threads Android est basée sur OpenThread et ot-br-posix
, qui sont publiés en Open Source par Google sur GitHub. De la même manière qu'OpenThread est développé dans un dépôt GitHub public, la pile Android Thread est développée dans le code source AOSP public. Toutes les fonctionnalités et corrections de bugs sont d'abord envoyées dans AOSP. Cela permet aux fournisseurs de commencer à adopter les dernières versions de Thread sans attendre les versions Android régulières.
Architecture
L'ensemble de la pile de threads Android se compose de deux composants principaux: la pile de threads principale dans une partition système générique et le service HAL de thread dans une partition du fournisseur. Les fournisseurs d'appareils ne doivent généralement s'occuper que de créer le service HAL.
Voici un bref résumé du fonctionnement de la pile Thread Android :
- Un service système Thread Java est présent dans le serveur système, qui gère l'ensemble de la pile. Il fournit l'API système Thread, crée l'interface de tunnel thread-wpan
, enregistre le réseau Thread auprès du service de connectivité et implémente les fonctionnalités de routage de bordure et de proxy publicitaire.
- La pile Thread / OpenThread principale est hébergée dans un processus natif autonome non privilégié nommé ot-daemon
. ot-daemon
est géré directement par le service système Java via des API AIDL privées et accède à la radio matérielle Thread via l'API Thread HAL.
- Un service HAL Thread fourni par le fournisseur DOIT implémenter l'API HAL Thread. Il fonctionne généralement en tant que RCP et implémente le protocole spinel.
Où se trouve le code ?
- Le framework / API et service Android Thread: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/thread/
- API Thread HAL et implémentation du service par défaut: https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/threadnetwork/
- Dépôt OpenThread importé: https://cs.android.com/android/platform/superproject/main/+/main:external/openthread/
- Répertoire ot-br-posix importé: https://cs.android.com/android/platform/superproject/main/+/main:external/ot-br-posix/
Configurer l'environnement de développement
Les fournisseurs d'appareils Android qui ont déjà établi un environnement de développement Android pour l'appareil peuvent ignorer cette section.
Si vous êtes nouveau dans l'écosystème Android ou que vous êtes un fournisseur de silicium qui souhaite rendre votre puce Thread compatible avec Android et fournir une assistance aux fournisseurs d'appareils, lisez la suite.
Suivez l'atelier de programmation pour les développeurs Android
Pour configurer votre environnement de développement Android pour la première fois, utilisez l'atelier de programmation suivant: https://source.android.com/docs/setup/start. À la fin de cet atelier de programmation, vous pourrez créer et exécuter un appareil Cuttlefish simulé à partir du code source.
Créer votre service HAL Thread
Essayer Thread dans Cuttlefish
Cuttlefish est l'appareil Android virtuel. Avant de commencer à créer votre propre service HAL, il est préférable d'essayer Thread dans Cuttlefish pour comprendre le fonctionnement de HAL.
Un service HAL Thread par défaut est fourni dans Cuttlefish et est implémenté avec le RCP simulé, qui transceiche des paquets via un socket UDP vers et depuis une radio Thread (802.15.4) simulée.
Dans l'instance Cuttlefish, une application "ThreadNetworkDemoApp" est préinstallée. Ouvrez cette application pour rejoindre l'appareil Cuttlefish à un réseau Thread par défaut.
Les outils de ligne de commande ot-ctl
et ot-cli-ftd
sont également fournis pour configurer votre réseau Thread à des fins de test. Ces outils sont compatibles avec toutes les commandes de ligne de commande OpenThread que vous connaissez peut-être déjà.
Vous pouvez rechercher des journaux du service HAL de thread Cuttlefish en procédant comme suit:
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
Vous pouvez également rechercher les journaux du daemon ot en effectuant les opérations suivantes:
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]
Le service HAL Thread de Cuttlefish utilise le service HAL Thread par défaut ainsi que le binaire RCP simulé OpenThread. Pour en savoir plus, consultez la section suivante.
Service HAL par défaut
Un service HAL par défaut est inclus avec l'API HAL Thread. Le service HAL par défaut est compatible avec les appareils RCP simulés et réels. Il reçoit une URL d'appareil RCP facultative. Si l'URL n'est pas fournie, l'appareil RCP simulé est utilisé par défaut.
Dans le fichier 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
Cela équivaut à:
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
Pour les appareils RCP réels, il est compatible avec les interfaces SPI et UART. Vous pouvez spécifier l'appareil avec les schémas spinel+spi://
, spinel+hdlc+uart://
et spinel+socket://
, respectivement.
Comprendre l'APEX du fournisseur
Comme la pile Thread dans le module principal de partage de connexion, le service HAL Thread par défaut de Cuttlefish est également empaqueté dans un module APEX. Toutefois, il s'agit d'un module APEX du fournisseur qui sera installé dans /vendor/apex/
(les artefacts du module seront décompressés dans /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 ], }
Vous devrez prêter attention à certaines configurations importantes ou apporter des modifications lorsque vous créerez votre propre module HAL APEX:
file_contexts
: décrit les fichiers binaires / de données fournis dans ce module APEX ou les fichiers auxquels le service HAL doit accéder (par exemple, l'appareil RCP). Cela vous permet de spécifier des règles sepolicy spécifiques pour que votre service HAL puisse accéder à l'appareil RCP matériel.binaries
: fichier binaire fourni dans ce module APEXthreadnetwork-service.rc
: mode de démarrage du service HAL. Vous devez spécifier le chemin d'accès de l'appareil RCP ici.android.hardware.thread_network.prebuilt.xml
: définit la fonctionnalité matérielleandroid.hardware.thread_network
. Cela permet au système Android de savoir que votre appareil est compatible avec le matériel Thread. Sinon, la pile de threads Android ne sera pas activée.
Créer votre service HAL
Que vous soyez développeur d'appareils Android ou fournisseur de silicium, vous devez savoir créer un micrologiciel OT RCP pour votre puce Thread. Les instructions suivantes supposent que la puce matérielle est correctement câblée et validée.
Le moyen le plus simple de compiler votre APEX HAL consiste à créer un nouvel APEX avec les binaires et les précompilés de l'APEX HAL par défaut. Par exemple, si votre entreprise est Banana et que l'appareil RCP de votre appareil est /dev/ttyACM0
, votre APEX HAL Thread se présente comme suit:
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
Les chemins d'accès des fichiers de la première colonne sont liés à /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 }
En supposant que vous créez un appareil nommé Orange, le répertoire de configuration spécifique à l'appareil se présente comme suit:
device/banana/orange/threadnetwork/ sepolicy/ Android.bp file_contexts manifest.json threadnetwork-default.xml threadnetwork-service.rc
Consultez la section suivante pour connaître les règles sepolicy à ajouter dans le sous-répertoire sepolicy/
.
Règles Sepolicy pour l'appareil RCP
Par défaut, votre service HAL Thread n'a pas accès à l'appareil RCP (par exemple, /dev/ttyACM0
). Des règles sepolicy personnalisées doivent être ajoutées au répertoire sepolicy/
.
Créez un fichier sepolicy/threadnetwork_hal.te
avec le contenu suivant:
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;
Réunir
Vous avez maintenant terminé presque tous les besoins de code pour ajouter Thread. La dernière étape consiste à ajouter les règles Thread HAL APEX et sepolicy à l'image de votre appareil.
Pour ce faire, ajoutez le code ci-dessous à la Makefile
de votre appareil (par exemple, device.mk
):
PRODUCT_PACKAGES += com.banana.hardware.threadnetwork BOARD_SEPOLICY_DIRS += device/banana/orange/threadnetwork/sepolicy
Si tout fonctionne, vous devriez maintenant voir le journal du service HAL de thread semblable à celui-ci:
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
Le journal ot-daemon
se présente comme suit:
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
Personnalisation
Le module principal Thread (qui fait partie du module "Tethering") fournit quelques configurations superposables que les fournisseurs peuvent spécifier pour personnaliser le comportement de la pile. Pour obtenir la liste complète, consultez config_thread.xml.
En règle générale, vous devez définir config_thread_border_router_default_enabled
sur true
pour activer votre appareil en tant que routeur de bordure Thread, et remplacer les valeurs config_thread_vendor_name
, config_thread_vendor_oui
et config_thread_model_name
par celles de votre fournisseur ou de votre produit. Ces valeurs seront incluses dans le service mDNS _meshcop._udp
, qui est toujours annoncé par un routeur de bordure Thread.
Pour ajouter la superposition, vous devez créer une cible ConnectivityOverlayOrange
runtime_resource_overlay pour votre appareil Orange. Créez un répertoire ConnectivityOverlay/
sous device/banana/orange/rro_overlays
et créez-y le contenu suivant:
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>
Comme pour l'APEX HAL, vous devez ajouter l'application en superposition à votre fichier device.mk
:
PRODUCT_PACKAGES += \ ConnectivityOverlayOrange</code>
Si tout fonctionne, vous verrez que ot-daemon
consigne le nom du fournisseur et du modèle au tout début du journal:
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
être compatible avec Google Home ;
En outre, si vous souhaitez que votre routeur de bordure soit utilisé par l'écosystème Google Home, vous pouvez spécifier cette configuration dans config_thread.xml
:
<string-array name="config_thread_mdns_vendor_specific_txts"> <item>vgh=1</item> </string-array>
Tests
Votre appareil devrait désormais être compatible avec la spécification du routeur de bordure Thread 1.3 ou version ultérieure. Avant de l'envoyer au programme de certification Thread, vous devez effectuer quelques tests xTS Android pour vous assurer de sa compatibilité.
Le test VTS vérifie que le service HAL Thread fonctionne comme prévu sur votre appareil. Vous pouvez exécuter les tests avec la commande
atest VtsHalThreadNetworkTargetTest
Le test CTS permet de s'assurer que les API Thread fonctionnent comme prévu sur votre appareil. Vous pouvez exécuter les tests avec la commande
atest CtsThreadNetworkTestCases
Le test d'intégration offre une garantie de qualité plus élevée du fonctionnement du code principal Thread sur votre appareil. Vous pouvez exécuter les tests avec la commande
atest ThreadNetworkIntegrationTests
Vous trouverez également d'autres instructions pour exécuter des tests VTS/CTS/MTS avec ces suites de tests publiées:
- https://source.android.com/docs/core/tests/vts
- https://source.android.com/docs/compatibility/cts/run
- https://docs.partner.android.com/mainline/test/mts (vous devez être partenaire pour accéder à ce lien)
Tester avec l'application de démonstration Thread
Comme pour l'appareil Cuttlefish, vous pouvez ajouter l'application de démonstration Thread à votre image système:
# ThreadNetworkDemoApp for testing PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp
Notez que vous ne devez l'ajouter qu'à la variante de débogage / eng (par exemple, PRODUCT_PACKAGES_DEBUG
), car elle ne doit pas être incluse dans le build utilisateur pour les consommateurs finaux.