Создайте пограничный маршрутизатор Android

Посмотреть исходный код в Android Code Search

Если вы не являетесь производителем устройств Android или чипов Thread, вы можете прекратить чтение прямо сейчас.

В этом документе описаны шаги по созданию нового устройства Thread Border Router на базе Android с новейшим исходным кодом AOSP. Следуя этому документу, вы узнаете:

  1. общая архитектура и статус поддержки потоков в Android
  2. как создать свой собственный сервис Thread HAL
  3. как сделать ваше устройство совместимым с Google Home
  4. как протестировать маршрутизатор Thread Border Router

Если вам нужна поддержка, сообщите о проблеме на GitHub или откройте обсуждение, если у вас есть вопросы.

Обзор

Стек Android Thread основан на OpenThread и ot-br-posix , исходный код которых открыт Google на GitHub. Точно так же, как OpenThread разрабатывается в публичном репозитории GitHub, так и стек Android Thread разрабатывается в общедоступной кодовой базе AOSP . Все функции и исправления ошибок сначала отправляются в AOSP. Это позволяет поставщикам начать внедрять последние версии Thread, не дожидаясь обычных выпусков Android.

Архитектура

Весь стек Android Thread состоит из двух основных компонентов: основного стека Thread в общем системном разделе и службы Thread HAL в разделе поставщика. Поставщикам устройств обычно нужно только позаботиться и создать службу HAL.

Android-поток-арка

Вот краткое описание того, как работает стек Android Thread: - На системном сервере имеется системная служба Java Thread, которая управляет всем стеком - предоставляет системный API Thread, создает туннельный интерфейс thread-wpan , регистрирует сеть Thread для службу подключения и реализует функции пограничной маршрутизации и рекламного прокси. — Основной стек Thread/OpenThread размещается в непривилегированном автономном собственном процессе, который называется ot-daemon . ot-daemon напрямую управляется системной службой Java через частные API AIDL и получает доступ к аппаратному радиомодулю Thread через API Thread HAL. - Предоставляемая поставщиком служба Thread HAL ДОЛЖНА реализовывать API Thread HAL. Обычно он работает как RCP и реализует шпинельный протокол.

Где код?

Настройка среды разработки

Поставщики устройств Android, которые уже установили среду разработки Android для устройства, могут пропустить этот раздел.

Если вы новичок в экосистеме Android или являетесь поставщиком микросхем, который хочет сделать свой чип Thread совместимым с Android и обеспечить поддержку производителей устройств, продолжайте читать.

Следуйте кодовой лаборатории разработчиков Android

Чтобы настроить среду разработки Android в первый раз, используйте следующую кодовую лабораторию: https://source.android.com/docs/setup/start . В конце этой лабораторной работы вы сможете создать и запустить смоделированное устройство каракатицы из исходного кода.

Создайте свой сервис Thread HAL

Попробуйте нить в каракатице

Каракатица — это виртуальное устройство Android. Прежде чем приступать к созданию собственного сервиса HAL, лучше попробовать Thread в Cuttlefish, чтобы понять, как работает HAL.

Служба HAL Thread по умолчанию предоставляется в Cuttlefish и реализована с помощью имитируемого RCP , который передает пакеты через сокет UDP в и из имитируемого радиоканала Thread (802.15.4).

В экземпляре Cuttlefish предварительно установлено приложение «ThreadNetworkDemoApp». Откройте это приложение, чтобы подключить устройство «Каракатица» к сети Thread по умолчанию.

скриншот демо-приложения

Также предусмотрены инструменты командной строки ot-ctl и ot-cli-ftd для настройки вашей сети Thread для тестирования. Эти инструменты поддерживают все команды OpenThread CLI, с которыми вы, возможно, уже знакомы.

Вы можете получить журналы службы HAL Cuttlefish Thread:

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]

Служба HAL Cuttlefish Thread использует службу HAL Thread по умолчанию, а также симулированный двоичный файл RCP OpenThread. О том, как она работает, см. в следующем разделе.

Служба HAL по умолчанию

Служба HAL по умолчанию включена вместе с API Thread HAL. Служба HAL по умолчанию поддерживает как смоделированные, так и реальные устройства RCP. Он получает дополнительный URL-адрес устройства RCP, и если URL-адрес не указан, по умолчанию используется имитируемое устройство 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

Подобно стеку Thread в основном модуле Tethering, служба HAL Thread по умолчанию в Cuttlefish также упакована в модуль 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 для доступа к аппаратному устройству RCP.

  • binaries : двоичный файл, поставляемый в этом модуле APEX.

  • threadnetwork-service.rc : как будет запущена служба HAL. Здесь вам необходимо указать путь к устройству RCP.

  • android.hardware.thread_network.prebuilt.xml : определяет аппаратную функцию android.hardware.thread_network . Это необходимо, чтобы система Android знала, что ваше устройство поддерживает аппаратную поддержку Thread. В противном случае стек Android Thread не будет включен.

Создайте свой сервис HAL

Независимо от того, являетесь ли вы разработчиком устройств Android или поставщиком микросхем, вы должны быть знакомы с созданием прошивки OT RCP для вашего чипа Thread. Следующие инструкции предполагают, что аппаратный чип правильно подключен и проверен.

Самый простой способ создать HAL APEX — создать новый APEX с двоичными файлами и предварительно созданными HAL APEX по умолчанию. Например, если ваша компания — Banana, а устройство RCP на вашем устройстве — /dev/ttyACM0 , ваш Thread 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/ .

Правила Sepolicy для устройства RCP

По умолчанию ваша служба Thread 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;

Собрать вместе

Теперь вы закончили почти весь код, необходимый для добавления Thread, и последний шаг — добавить Thread HAL APEX и правила sepolicy в образ вашего устройства.

Вы можете сделать это, добавив приведенный ниже код в Makefile вашего устройства (например, device.mk ):

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

Если все работает, теперь вы сможете увидеть журнал службы Thread 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

Кастомизация

Основной модуль Thread (на самом деле это часть модуля «Привязка») предоставляет несколько накладываемых конфигураций , которые могут быть указаны поставщиками для настройки поведения стека. Полный список см. в config_thread.xml .

Обычно вам необходимо изменить config_thread_vendor_name , config_thread_vendor_oui и config_thread_model_name на значения вашего поставщика или продукта. Эти значения будут включены в службу mDNS _meshcop._udp , которая всегда объявляется пограничным маршрутизатором потока.

Чтобы добавить наложение, вам необходимо создать новую цель ConnectivityOverlayOrange runtime_resource_overlay для вашего устройства Orange. Создайте новый каталог ConnectivityOverlay/ в разделе device/banana/orange/rro_overlays и создайте в нем следующее содержимое:

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 :
  <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 гарантирует, что служба Thread HAL работает на вашем устройстве должным образом. Вы можете запустить тесты с помощью команды

    atest VtsHalThreadNetworkTargetTest

  • Тест CTS гарантирует, что API потоков работают на вашем устройстве должным образом. Вы можете запустить тесты с помощью команды

    atest CtsThreadNetworkTestCases

  • Интеграционный тест обеспечивает более качественную гарантию того, как основной код Thread работает на вашем устройстве. Вы можете запустить тесты с помощью команды

    atest ThreadNetworkIntegrationTests

Вы также можете найти дополнительные инструкции по запуску тестов VTS/CTS/MTS с помощью этих выпущенных наборов тестов:

Протестируйте с помощью демонстрационного приложения Thread

Как и в случае с устройством Cuttlefish, вы можете добавить демонстрационное приложение Thread в образ своей системы:

# ThreadNetworkDemoApp for testing
PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp

Обратите внимание, что вам следует добавить его только в вариант debug/eng (например, PRODUCT_PACKAGES_DEBUG ), поскольку его не предполагается включать в пользовательскую сборку для конечных потребителей.