Android 보더 라우터 빌드

Android 코드 검색에서 소스 보기

Android 기기 또는 Thread 칩 공급업체가 아닌 경우 지금 바로 읽기를 중단하셔도 됩니다.

이 문서에서는 최신 AOSP 소스 코드로 새로운 Android 기반 Thread Border Router 기기를 빌드하는 단계를 안내합니다. 이 문서를 따르면 다음을 알아볼 수 있습니다.

  1. Android의 스레드 지원에 관한 전반적인 아키텍처 및 상태
  2. 스레드 HAL 서비스를 만드는 방법
  3. 기기를 Google Home과 호환시키는 방법
  4. 스레드 보더 라우터 테스트 방법

지원이 필요한 경우 GitHub에서 문제를 신고하거나 질문이 있는 경우 토론을 시작하세요.

개요

Android 스레드 스택은 Google에서 GitHub에 오픈소스로 제공하는 OpenThread 및 ot-br-posix를 기반으로 합니다. OpenThread가 공개 GitHub 저장소에서 개발되는 것과 마찬가지로 Android 스레드 스택은 공개 AOSP 코드베이스에서 개발됩니다. 모든 기능과 버그 수정은 먼저 AOSP에 제출됩니다. 이를 통해 공급업체는 정규 Android 출시를 기다리지 않고도 최신 스레드 버전을 도입할 수 있습니다.

아키텍처

전체 Android 스레드 스택은 두 가지 주요 구성요소로 구성됩니다. 일반 시스템 파티션의 핵심 스레드 스택과 공급업체 파티션의 스레드 HAL 서비스입니다. 기기 공급업체는 일반적으로 HAL 서비스를 관리하고 빌드하기만 하면 됩니다.

android-thread-arch

다음은 Android 스레드 스택의 작동 방식을 간략하게 요약한 내용입니다. - 시스템 서버에는 전체 스택을 관리하는 Java 스레드 시스템 서비스가 있습니다. 이 서비스는 스레드 시스템 API를 제공하고, thread-wpan 터널 인터페이스를 만들고, 스레드 네트워크를 연결 서비스에 등록하고, 경계 라우팅 및 광고 프록시 기능을 구현합니다. - 핵심 스레드 / OpenThread 스택은 권한이 없는 독립형 네이티브 프로세스(이름: ot-daemon)에서 호스팅됩니다. ot-daemon는 비공개 AIDL API를 통해 Java 시스템 서비스에서 직접 관리되며 Thread HAL API를 통해 스레드 하드웨어 라디오에 액세스합니다. - 공급업체 제공 Thread HAL 서비스는 Thread HAL API를 구현해야 합니다(MUST). 일반적으로 RCP로 작동하며 스피넬 프로토콜을 구현합니다.

코드는 어디에 있나요?

개발 환경 설정

기기의 Android 개발 환경을 이미 설정한 Android 기기 공급업체는 이 섹션을 건너뛰어도 됩니다.

Android 생태계에 익숙하지 않거나 Thread 칩을 Android와 호환되도록 만들고 기기 공급업체를 지원하려는 칩 공급업체인 경우 계속 읽어보세요.

Android 개발자 Codelab 따라하기

Android 개발 환경을 처음 설정하려면 다음 Codelab(https://source.android.com/docs/setup/start)을 사용하세요. 이 Codelab을 마치면 소스 코드에서 시뮬레이션된 Cuttlefish 기기를 빌드하고 실행할 수 있습니다.

스레드 HAL 서비스 빌드

Cuttlefish에서 Thread 사용해 보기

Cuttlefish는 가상 Android 기기입니다. 자체 HAL 서비스를 빌드하기 전에 Cuttlefish에서 Thread를 사용하여 HAL의 작동 방식을 이해하는 것이 좋습니다.

기본 Thread HAL 서비스는 Cuttlefish에서 제공되며, UDP 소켓을 통해 시뮬레이션된 Thread (802.15.4) 무선으로 패킷을 송수신하는 시뮬레이션된 RCP로 구현됩니다.

Cuttlefish 인스턴스에는 'ThreadNetworkDemoApp'이 사전 설치되어 있습니다. 이 앱을 열어 Cuttlefish 기기를 기본 스레드 네트워크에 연결합니다.

demoapp-screenshot

테스트를 위해 Thread 네트워크를 구성하는 데 제공되는 ot-ctlot-cli-ftd 명령줄 도구도 있습니다. 이러한 도구는 이미 익숙할 수 있는 모든 OpenThread CLI 명령어를 지원합니다.

다음과 같이 Cuttlefish Thread HAL 서비스의 로그를 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

또는 다음과 같이 ot-daemon 로그를 grep합니다.

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]

Cuttlefish Thread HAL 서비스는 기본 Thread HAL 서비스와 OpenThread 시뮬레이션된 RCP 바이너리를 사용합니다. 작동 방식은 다음 섹션을 참고하세요.

기본 HAL 서비스

기본 HAL 서비스가 Thread HAL API와 함께 포함되어 있습니다. 기본 HAL 서비스는 시뮬레이션된 RCP 기기와 실제 RCP 기기를 모두 지원합니다. 선택적 RCP 기기 URL을 수신하며 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 이해

테더링 메인라인 모듈의 스레드 스택과 마찬가지로 Cuttlefish의 기본 스레드 HAL 서비스도 APEX 모듈에 패키징됩니다. 하지만 /vendor/apex/에 설치되는 공급업체 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 기기에 액세스할 수 있도록 특정 sepolicy 규칙을 지정할 수 있습니다.

  • binaries: 이 APEX 모듈에서 제공되는 바이너리 파일입니다.

  • threadnetwork-service.rc: HAL 서비스가 시작되는 방식입니다. 여기에서 RCP 기기 경로를 지정해야 합니다.

  • android.hardware.thread_network.prebuilt.xml: android.hardware.thread_network 하드웨어 기능을 정의합니다. 이는 Android 시스템이 기기에 스레드 하드웨어 지원이 있는지 알 수 있도록 하기 위한 것입니다. 그렇지 않으면 Android 스레드 스택이 사용 설정되지 않습니다.

HAL 서비스 만들기

Android 기기 개발자이든 칩 공급업체이든 Thread 칩용 OT RCP 펌웨어 빌드에 익숙해야 합니다. 다음 안내는 하드웨어 칩이 올바르게 배선되고 유효성을 검사했다고 가정합니다.

HAL APEX를 빌드하는 가장 간단한 방법은 기본 HAL APEX의 바이너리 및 사전 빌드된 항목으로 새 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 규칙은 다음 섹션을 참고하세요.

RCP 기기의 Sepolicy 규칙

기본적으로 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;

종합

이제 스레드 추가에 필요한 거의 모든 코드를 완료했습니다. 마지막 단계는 기기의 이미지에 스레드 HAL APEX 및 sepolicy 규칙을 추가하는 것입니다.

이렇게 하려면 기기의 Makefile (예: device.mk)에 아래 코드를 추가하면 됩니다.

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

모든 것이 제대로 작동하면 다음과 유사한 스레드 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

맞춤설정

스레드 메인라인 모듈 (실제로는 '테더링' 모듈의 일부)은 공급업체가 스택 동작을 맞춤설정하기 위해 지정할 수 있는 몇 가지 오버레이 가능한 구성을 제공합니다. 전체 목록은 config_thread.xml을 참고하세요.

일반적으로 기기를 스레드 보더 라우터로 사용 설정하려면 config_thread_border_router_default_enabledtrue로 설정하고 config_thread_vendor_name, config_thread_vendor_oui, config_thread_model_name를 공급업체 또는 제품 값으로 변경해야 합니다. 이러한 값은 항상 스레드 경계 라우터에서 광고하는 _meshcop._udp mDNS 서비스에 포함됩니다.

오버레이를 추가하려면 Orange 기기용 새 ConnectivityOverlayOrange runtime_resource_overlay 타겟을 만들어야 합니다. device/banana/orange/rro_overlays 아래에 새 ConnectivityOverlay/ 디렉터리를 만들고 아래 콘텐츠를 만듭니다.

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>
  

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 생태계에서 Border Router를 사용하도록 하려면 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 테스트는 Thread API가 기기에서 예상대로 작동하는지 확인합니다. 다음 명령어를 사용하여 테스트를 실행할 수 있습니다.

    atest CtsThreadNetworkTestCases

  • 통합 테스트는 스레드 메인라인 코드가 기기에서 작동하는 방식에 대한 품질 보증을 강화합니다. 다음 명령어로 테스트를 실행할 수 있습니다.

    atest ThreadNetworkIntegrationTests

이러한 출시된 테스트 모음으로 VTS/CTS/MTS 테스트를 실행하는 방법에 관한 자세한 안내는 다음을 참고하세요.

Thread 데모 앱으로 테스트

Cuttlefish 기기와 마찬가지로 시스템 이미지에 Thread 데모 앱을 추가할 수 있습니다.

# ThreadNetworkDemoApp for testing
PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp

이는 최종 소비자용 사용자 빌드에 포함되어서는 안 되므로 디버그 / eng 변형 (예: PRODUCT_PACKAGES_DEBUG)에만 추가해야 합니다.