Ver a origem na Pesquisa de código do Android
Se você não é um fornecedor de dispositivos Android ou de chips Thread, pode parar de ler agora.
Este documento orienta você nas etapas para criar um novo dispositivo de roteador de borda Thread baseado no Android com o código-fonte AOSP mais recente. Ao seguir este documento, você vai aprender:
- a arquitetura geral e o status do suporte a Thread no Android
- como criar seu próprio serviço HAL do Thread
- como tornar seu dispositivo compatível com o Google Home
- como testar o roteador de borda do Thread
Se precisar de suporte, registre um problema no GitHub ou abra uma discussão se tiver dúvidas.
Visão geral
A pilha de linhas de execução do Android é baseada no OpenThread e no ot-br-posix
, que são
de código aberto do Google no GitHub. Da mesma forma que o OpenThread é desenvolvido em um
repositório público do GitHub, a pilha do Android Thread é desenvolvida na
base de código pública do AOSP. Todos os recursos e correções de bugs
são enviados primeiro no AOSP. Isso permite que os fornecedores comecem a adotar as versões
mais recentes do Thread sem esperar pelas versões regulares do Android.
Arquitetura
A pilha de linhas de execução do Android consiste em dois componentes principais: a pilha de linhas de execução principal em uma partição de sistema genérica e o serviço HAL de linha de execução em uma partição do fornecedor. Os fornecedores de dispositivos geralmente precisam apenas cuidar e criar o serviço HAL.
Confira um breve resumo de como a pilha de linhas de execução do Android funciona:
- Há um serviço de sistema de linha de execução Java no servidor do sistema que gerencia toda a
pilha, fornece a API do sistema de linha de execução, cria a interface de túnel thread-wpan
,
registra a rede Thread no
serviço de conectividade
e implementa as funcionalidades de roteamento de borda e proxy de publicidade.
- A pilha Thread / OpenThread principal é hospedada em um processo nativo
autônomo sem privilégios chamado ot-daemon
. O ot-daemon
é gerenciado diretamente
pelo serviço de sistema Java usando APIs AIDL privadas e acessa o rádio de hardware
do Thread pela API HAL do Thread.
- Um serviço HAL do Thread fornecido pelo fornecedor PRECISA implementar a API HAL do Thread. Ele
geralmente funciona como um
RCP
e implementa o protocolo
spinel.
Onde está o código?
- O framework / API e serviço do Android Thread: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/thread/
- A API HAL do Thread e a implementação de serviço padrão: https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/threadnetwork/
- Repositório do OpenThread importado: https://cs.android.com/android/platform/superproject/main/+/main:external/openthread/
- Repositório ot-br-posix importado: https://cs.android.com/android/platform/superproject/main/+/main:external/ot-br-posix/
Configurar o ambiente de desenvolvimento
Os fornecedores de dispositivos Android que já estabeleceram um ambiente de desenvolvimento Android para o dispositivo podem pular esta seção.
Se você é novo no ecossistema do Android ou é um fornecedor de silício que quer tornar seu chip Thread compatível com o Android e oferecer suporte a fornecedores de dispositivos, continue lendo.
Siga o codelab para desenvolvedores Android
Para configurar o ambiente de desenvolvimento Android pela primeira vez, use o codelab a seguir: https://source.android.com/docs/setup/start. Ao final deste codelab, você vai conseguir criar e executar um dispositivo Cuttlefish simulado a partir do código-fonte.
Criar o serviço HAL do Thread
Testar a conversa no Cuttlefish
O Cuttlefish é o dispositivo Android virtual. Antes de começar a criar seu próprio serviço HAL, é melhor testar a linha de execução no Cuttlefish para entender como o HAL funciona.
Um serviço HAL de padrão de Thread é fornecido no Cuttlefish e implementado com o RCP simulado, que recebe e envia pacotes via soquete UDP para e de um rádio Thread (802.15.4) simulado.
Na instância do Cuttlefish, um "ThreadNetworkDemoApp" é pré-instalado. Abra esse app para conectar o dispositivo Cuttlefish a uma rede Thread padrão.
Também há as ferramentas de linha de comando ot-ctl
e ot-cli-ftd
para
configurar a rede Thread para testes. Essas ferramentas oferecem suporte a todos os
comandos da CLI do OpenThread que você já conhece.
É possível pesquisar registros do serviço HAL da linha Cuttlefish da seguinte maneira:
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
Ou use o grep para encontrar registros do daemon ot:
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]
O serviço HAL de linhagem Cuttlefish usa o serviço HAL de linhagem padrão e o binário RCP simulado do OpenThread. Consulte a próxima seção para saber como ele funciona.
O serviço HAL padrão
Um serviço HAL padrão é incluído com a API HAL do Thread. O serviço HAL padrão oferece suporte a dispositivos RCP simulados e reais. Ele recebe um URL de dispositivo RCP opcional e, se o URL não for fornecido, ele será definido como o dispositivo RCP simulado.
No arquivo 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
Isso é equivalente 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
Para dispositivos RCP reais, ele oferece suporte às interfaces SPI e UART, e você pode
especificar o dispositivo com o esquema spinel+spi://
, spinel+hdlc+uart://
e
spinel+socket://
, respectivamente.
Entender o APEX do fornecedor
Semelhante à pilha de linhas de execução no módulo principal de tethering, o serviço HAL de linha de execução
padrão no Cuttlefish também é empacotado em um módulo APEX. No entanto, é um
módulo APEX do fornecedor que será instalado em /vendor/apex/
. Os artefatos no
módulo serão descompactados em /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 ], }
Há algumas configurações importantes que você precisa prestar atenção ou fazer alterações ao criar seu próprio módulo HAL APEX:
file_contexts
: descreve os arquivos binários / de dados enviados neste módulo APEX ou os arquivos que o serviço HAL precisa acessar (por exemplo, o dispositivo RCP). Isso permite especificar regras de política de segurança específicas para que seu serviço HAL acesse o dispositivo RCP de hardware.binaries
: o arquivo binário entregue neste módulo do APEXthreadnetwork-service.rc
: como o serviço HAL será iniciado. É necessário especificar o caminho do dispositivo RCP aqui.android.hardware.thread_network.prebuilt.xml
: define o recurso de hardwareandroid.hardware.thread_network
. Isso é necessário para que o sistema Android saiba que o dispositivo tem suporte a hardware Thread. Caso contrário, a pilha de linhas de execução do Android não será ativada.
Criar o serviço HAL
Seja você um desenvolvedor de dispositivos Android ou um fornecedor de silício, é importante conhecer a criação de firmware OT RCP para seu chip Thread. As instruções a seguir pressupõem que o chip de hardware está conectado e validado corretamente.
A maneira mais simples de criar seu HAL APEX é criar um novo APEX com os
binários e pré-criados do HAL APEX padrão. Por exemplo, se a sua empresa for
Banana e o dispositivo RCP no seu dispositivo for /dev/ttyACM0
, o APEX
HAL do Thread vai ficar assim:
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
Os caminhos dos arquivos na primeira coluna estão relacionados 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 }
Supondo que você esteja criando um novo dispositivo chamado "Laranja", o diretório de configuração específico do dispositivo será:
device/banana/orange/threadnetwork/ sepolicy/ Android.bp file_contexts manifest.json threadnetwork-default.xml threadnetwork-service.rc
Consulte a próxima seção para saber quais regras de sepolicy precisam ser adicionadas no subdiretório
sepolicy/
.
Regras de Sepolicy para dispositivos RCP
Por padrão, o serviço HAL do Thread não tem acesso ao dispositivo RCP (por
exemplo, /dev/ttyACM0
). As regras de política de segurança personalizadas precisam ser adicionadas ao
diretório sepolicy/
.
Crie um novo arquivo sepolicy/threadnetwork_hal.te
com o conteúdo abaixo:
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;
Reunir
Agora que você terminou quase todas as necessidades de código para adicionar a Thread, a última etapa é adicionar o APEX do HAL da Thread e as regras de sepolicy à imagem do dispositivo.
Para fazer isso, adicione o código abaixo à Makefile
do dispositivo (por exemplo,
device.mk
):
PRODUCT_PACKAGES += com.banana.hardware.threadnetwork BOARD_SEPOLICY_DIRS += device/banana/orange/threadnetwork/sepolicy
Se tudo funcionar, você vai poder conferir o registro de serviço HAL do Thread semelhante a este:
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
E o registro ot-daemon
será assim:
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
Personalização
O módulo principal da Thread (que faz parte do módulo "Tethering") oferece algumas configurações sobrepostas que podem ser especificadas pelos fornecedores para personalizar o comportamento da pilha. Consulte config_thread.xml para conferir a lista completa.
Normalmente, é necessário definir config_thread_border_router_default_enabled
como true
para ativar o dispositivo como um roteador de borda do Thread e mudar o
config_thread_vendor_name
, config_thread_vendor_oui
e
config_thread_model_name
para os valores do fornecedor ou do produto. Esses valores serão
incluídos no serviço mDNS _meshcop._udp
, que é sempre anunciado por um
roteador de borda Thread.
Para adicionar a sobreposição, crie uma nova meta ConnectivityOverlayOrange
runtime_resource_overlay para o dispositivo Orange. Crie um novo
diretório ConnectivityOverlay/
em device/banana/orange/rro_overlays
e
crie o conteúdo abaixo nele:
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>
Assim como no HAL APEX, é necessário adicionar o app de sobreposição ao arquivo
device.mk
:
PRODUCT_PACKAGES += \ ConnectivityOverlayOrange</code>
Se tudo funcionar, você vai notar que ot-daemon
registra o nome do fornecedor e do modelo
no início do registro:
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
Ser compatível com o Google Home
Além disso, se você quiser que o roteador de borda seja usado pelo ecossistema do Google Home, especifique essa configuração em config_thread.xml
:
<string-array name="config_thread_mdns_vendor_specific_txts"> <item>vgh=1</item> </string-array>
Teste
Seu dispositivo precisa ser compatível com a especificação do roteador de borda do Thread 1.3 ou mais recente. Antes de enviar ao programa de certificação do Thread, é necessário realizar alguns testes do xTS do Android para garantir a compatibilidade.
O teste VTS garante que o serviço HAL do Thread funcione conforme o esperado no dispositivo. É possível executar os testes com o comando
atest VtsHalThreadNetworkTargetTest
O teste do CTS garante que as APIs do Thread funcionem conforme o esperado no seu dispositivo. É possível executar os testes com o comando
atest CtsThreadNetworkTestCases
O teste de integração oferece mais garantia de qualidade sobre como o código principal da linha de execução da Thread funciona no seu dispositivo. É possível executar os testes com o comando
atest ThreadNetworkIntegrationTests
Você também pode encontrar mais instruções sobre como executar testes VTS/CTS/MTS com esses conjuntos de testes lançados:
- https://source.android.com/docs/core/tests/vts
- https://source.android.com/docs/compatibility/cts/run
- https://docs.partner.android.com/mainline/test/mts (é necessário ser um parceiro para acessar este link)
Testar com o app de demonstração da linha de execução
Assim como no dispositivo Cuttlefish, é possível adicionar o app de demonstração de linha de execução à imagem do sistema:
# ThreadNetworkDemoApp for testing PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp
Adicione-o apenas à variante de depuração / eng (por exemplo,
PRODUCT_PACKAGES_DEBUG
), já que ele não pode ser incluído no build
do usuário para consumidores finais.