Creare un router di confine Android

Visualizza origine su Android Code Search

Se non sei un produttore di dispositivi Android o chip Thread, puoi smettere di leggere subito.

Questo documento illustra la procedura per creare un nuovo dispositivo Thread Border Router basato su Android con il codice sorgente AOSP più recente. Seguendo questo documento, imparerai:

  1. l'architettura e lo stato generale del supporto di Thread in Android
  2. Come creare il tuo servizio HAL Thread
  3. Come rendere il tuo dispositivo compatibile con Google Home
  4. Come testare il router di confine Thread

Se hai bisogno di assistenza, invia una segnalazione su GitHub o apri una discussione se hai domande.

Panoramica

Lo stack Thread di Android si basa su OpenThread e ot-br-posix, che sono sviluppati in open source da Google su GitHub. Come OpenThread viene sviluppato in un repository GitHub pubblico, lo stack Android Thread viene sviluppato nel codebase AOSP pubblico. Tutte le funzionalità e le correzioni di bug vengono inviate prima in AOSP. In questo modo, i fornitori possono iniziare ad adottare le ultime versioni di Thread senza attendere le release regolari di Android.

Architettura

L'intero stack Thread di Android è costituito da due componenti principali: lo stack Thread di base in una partizione di sistema generica e il servizio HAL Thread in una partizione del fornitore. In genere, i fornitori di dispositivi devono solo occuparsi e creare il servizio HAL.

android-thread-arch

Ecco un breve riepilogo del funzionamento dello stack Thread di Android: - Nel server di sistema è presente un servizio di sistema Thread Java che gestisce l'intero stack, fornisce l'API di sistema Thread, crea l'interfaccia del tunnel thread-wpan, registra la rete Thread nel servizio di connettività e implementa le funzionalità di Border Routing e Advertising Proxy. - Lo stack Thread / OpenThread principale è ospitato in un processo nativo autonomo non privilegiato denominato ot-daemon. ot-daemon è gestito direttamente dal servizio di sistema Java tramite API AIDL private e accede alla radio hardware Thread tramite l'API HAL Thread. - Un servizio HAL Thread fornito dal fornitore DEVE implementare l'API HAL Thread. In genere, funziona come RCP e implementa il protocollo spinel.

Dove si trova il codice?

Configura l'ambiente di sviluppo

I fornitori di dispositivi Android che hanno già creato un ambiente di sviluppo Android per il dispositivo possono saltare questa sezione.

Se non hai dimestichezza con l'ecosistema Android o sei un fornitore di silicio che vuole trovare una soluzione per rendere il proprio chip Thread compatibile con Android e fornire assistenza ai fornitori di dispositivi, continua a leggere.

Segui il codelab per sviluppatori Android

Per configurare l'ambiente di sviluppo Android per la prima volta, utilizza il seguente codelab: https://source.android.com/docs/setup/start. Al termine di questo codelab, potrai creare ed eseguire un dispositivo Cuttlefish simulato dal codice sorgente.

Crea il servizio HAL Thread

Provare Thread in Cuttlefish

Cuttlefish è il dispositivo Android virtuale. Prima di iniziare a creare il tuo servizio HAL, è meglio provare Thread in Cuttlefish per capire come funziona HAL.

In Cuttlefish è fornito un servizio HAL Thread predefinito ed è implementato con il RCP simulato che trasmette e riceve pacchetti tramite socket UDP verso e da una radio Thread (802.15.4) simulata.

Nell'istanza Cuttlefish è preinstallata un'app "ThreadNetworkDemoApp". Apri l'app per collegare il dispositivo Cuttlefish a una rete Thread predefinita.

demoapp-screenshot

Sono inoltre disponibili gli strumenti a riga di comando ot-ctl e ot-cli-ftd per configurare la rete Thread per i test. Questi strumenti supportano tutti i comandi della CLI OpenThread che potresti già conoscere.

Puoi cercare i log del servizio HAL Thread di Cuttlefish utilizzando grep:

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

In alternativa, cerca i log di ot-daemon utilizzando:

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]

Il servizio HAL Thread di Cuttlefish utilizza il servizio HAL Thread predefinito più il programma binario RCP simulato di OpenThread. Per scoprire come funziona, consulta la sezione successiva.

Il servizio HAL predefinito

Un servizio HAL predefinito è incluso insieme all'API HAL Thread. Il servizio HAL predefinito supporta sia i dispositivi RCP simulati sia quelli reali. Riceve un URL del dispositivo RCP facoltativo e, se l'URL non viene fornito, viene utilizzato per impostazione predefinita il dispositivo RCP simulato.

Nel file 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

Ciò equivale a:

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

Per i dispositivi RCP reali, supporta sia le interfacce SPI sia UART e puoi specificare il dispositivo rispettivamente con gli schemi spinel+spi://, spinel+hdlc+uart:// e spinel+socket://.

Informazioni sull'APEX del fornitore

Analogamente allo stack Thread nel modulo principale del tethering, il servizio Thread HAL predefinito in Cuttlefish è pacchettizzato anche in un modulo APEX. Tuttavia, si tratta di un modulo APEX del fornitore che verrà installato in /vendor/apex/ (gli elementi nel modulo verranno decompressi in /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
    ],
}

Esistono alcune configurazioni importanti a cui devi prestare attenzione o apportare modifiche quando crei il tuo modulo HAL APEX:

  • file_contexts: descrive i file di dati / binari caricati in questo modulo APEX o i file a cui il servizio HAL deve accedere (ad esempio il dispositivo RCP). In questo modo puoi specificare regole sepolicy specifiche per il servizio HAL per accedere al dispositivo RCP hardware.

  • binaries: il file binario fornito in questo modulo APEX

  • threadnetwork-service.rc: la modalità di avvio del servizio HAL. Devi specificare il percorso del dispositivo RCP qui.

  • android.hardware.thread_network.prebuilt.xml: definisce la funzionalità hardware android.hardware.thread_network. Questo è necessario per consentire al sistema Android di sapere che il tuo dispositivo supporta l'hardware Thread. In caso contrario, lo stack Thread di Android non verrà attivato.

Crea il servizio HAL

Che tu sia uno sviluppatore di dispositivi Android o un fornitore di silicio, devi conoscere la creazione del firmware OT RCP per il tuo chip Thread. Le seguenti istruzioni presuppongono che il chip hardware sia cablato e convalidato correttamente.

Il modo più semplice per compilare il tuo APEX HAL è creare un nuovo APEX con i file binari e precompilati dell'APEX HAL predefinito. Ad esempio, se la tua azienda è Banana e il dispositivo RCP sul tuo dispositivo è /dev/ttyACM0, il tuo Thread HAL APEX sarà simile al seguente:

  • 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

I percorsi file nella prima colonna sono relativi a /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
  }

Supponendo che tu stia creando un nuovo dispositivo denominato Arancione, la directory di configurazione specifica del dispositivo sarà simile alla seguente:

device/banana/orange/threadnetwork/
    sepolicy/
    Android.bp
    file_contexts
    manifest.json
    threadnetwork-default.xml
    threadnetwork-service.rc

Consulta la sezione successiva per sapere quali regole sepolicy devono essere aggiunte nella sottodirectory sepolicy/.

Regole Sepolicy per il dispositivo RCP

Per impostazione predefinita, il servizio HAL Thread non ha accesso al dispositivo RCP (ad esempio /dev/ttyACM0), pertanto è necessario aggiungere regole sepolicy personalizzate alla directory sepolicy/.

Crea un nuovo file sepolicy/threadnetwork_hal.te con i seguenti contenuti:

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;

Mettere insieme

Ora hai completato quasi tutte le esigenze di codice per l'aggiunta di Thread. L'ultimo passaggio consiste nell'aggiungere le regole sepolicy e APEX HAL di Thread all'immagine del dispositivo.

Aggiungi il codice riportato di seguito a Makefile del tuo dispositivo (ad esempio, device.mk):

PRODUCT_PACKAGES += com.banana.hardware.threadnetwork
BOARD_SEPOLICY_DIRS += device/banana/orange/threadnetwork/sepolicy

Se tutto funziona, ora potrai vedere il log del servizio HAL Thread simile al seguente:

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

Il log ot-daemon sarà simile al seguente:

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

Personalizzazione

Il modulo Thread principale (in realtà fa parte del modulo "Tethering") fornisce alcune configurazioni sovrapponibili che possono essere specificate dai fornitori per personalizzare il comportamento dello stack. Consulta config_thread.xml per l'elenco completo.

In genere, devi impostare config_thread_border_router_default_enabled su true per attivare il dispositivo come router di confine Thread e modificare config_thread_vendor_name, config_thread_vendor_oui e config_thread_model_name in base ai valori del fornitore o del prodotto. Questi valori verranno inclusi nel servizio mDNS _meshcop._udp, che viene sempre pubblicizzato da un router di confine Thread.

Per aggiungere l'overlay, devi creare un nuovo target ConnectivityOverlayOrange runtime_resource_overlay per il tuo dispositivo Orange. Crea una nuova directory ConnectivityOverlay/ in device/banana/orange/rro_overlays e inserisci i seguenti contenuti:

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>
  

Come per HAL APEX, devi aggiungere l'app in overlay al file device.mk:

PRODUCT_PACKAGES += \
    ConnectivityOverlayOrange</code>

Se tutto funziona, vedrai che ot-daemon registra il nome del fornitore e del modello all'inizio del log:

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

Essere compatibile con Google Home

Inoltre, se vuoi che il tuo router di confine venga utilizzato dall'ecosistema Google Home, puoi specificare questa configurazione in config_thread.xml:

<string-array name="config_thread_mdns_vendor_specific_txts">
  <item>vgh=1</item>
</string-array>

Test

Il tuo dispositivo dovrebbe ora essere compatibile con la specifica del router di confine Thread 1.3 o versioni successive. Prima di inviarlo al programma di certificazione Thread, devono essere eseguiti alcuni test xTS Android per garantire la compatibilità.

  • Il test VTS garantisce che il servizio HAL Thread funzioni come previsto sul dispositivo. Puoi eseguire i test con il comando

    atest VtsHalThreadNetworkTargetTest

  • Il test CTS garantisce che le API Thread funzionino come previsto sul dispositivo. Puoi eseguire i test con il comando

    atest CtsThreadNetworkTestCases

  • Il test di integrazione offre una maggiore garanzia di qualità del funzionamento del codice di Thread mainline sul tuo dispositivo. Puoi eseguire i test con il comando

    atest ThreadNetworkIntegrationTests

Puoi anche trovare ulteriori istruzioni su come eseguire i test VTS/CTS/MTS con queste suite di test rilasciate:

Eseguire il test con l'app demo Thread

Come per il dispositivo Cuttlefish, puoi aggiungere l'app di dimostrazione Thread all'immagine di sistema:

# ThreadNetworkDemoApp for testing
PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp

Tieni presente che devi aggiungerlo solo alla variante di debug / eng (ad es.PRODUCT_PACKAGES_DEBUG), poiché non deve essere inclusa nella build dell'utente per i consumatori finali.