Android デバイスまたは Thread チップ ベンダーでない場合は、ここで読み進める必要はありません。
このドキュメントでは、最新の AOSP ソースコードを使用して、新しい Android ベースの Thread Border Router デバイスをビルドする手順について説明します。このドキュメントでは、次のことを学習します。
- Android でのスレッド サポートの全体的なアーキテクチャとステータス
- 独自の Thread HAL サービスを作成する方法
- デバイスを Google Home に対応させる方法
- Thread ボーダー ルーターをテストする方法
サポートが必要な場合は、GitHub で問題を報告するか、質問がある場合はディスカッションを開始してください。
概要
Android スレッド スタックは、Google が GitHub でオープンソース化した OpenThread と ot-br-posix
に基づいています。OpenThread が一般公開されている GitHub リポジトリで開発されているように、Android Thread スタックは一般公開されている AOSP コードベースで開発されています。すべての機能とバグ修正は、まず AOSP に送信されます。これにより、ベンダーは Android の通常のリリースを待たずに、最新の Thread バージョンの導入を開始できます。
アーキテクチャ
Android スレッド スタック全体は、汎用システム パーティションのコア スレッド スタックとベンダー パーティションのスレッド HAL サービスの 2 つの主要コンポーネントで構成されています。通常、デバイス ベンダーは HAL サービスを作成するだけで済みます。
Android Thread スタックの仕組みを簡単に説明すると、次のようになります。システム サーバーに、スタック全体を管理する Java Thread システム サービスがあります。このサービスは、Thread システム API を提供し、thread-wpan
トンネル インターフェースを作成し、Thread ネットワークを接続サービスに登録し、境界ルーティングと広告プロキシ機能を実装します。- コア Thread / OpenThread スタックは、ot-daemon
という名前の権限のないスタンドアロン ネイティブ プロセスでホストされます。ot-daemon
は、限定公開の AIDL API を介して Java システム サービスによって直接管理され、Thread HAL API を介して Thread ハードウェア無線にアクセスします。- ベンダー提供の Thread HAL サービスは、Thread HAL API を実装する必要があります。通常は RCP として機能し、spinel プロトコルを実装します。
コードの場所
- Android スレッド フレームワーク / API とサービス: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/thread/
- Thread HAL API とデフォルト サービスの実装: https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/threadnetwork/
- インポートされた OpenThread リポジトリ: https://cs.android.com/android/platform/superproject/main/+/main:external/openthread/
- インポートされた ot-br-posix リポジトリ: https://cs.android.com/android/platform/superproject/main/+/main:external/ot-br-posix/
開発環境をセットアップする
デバイスの Android 開発環境をすでに構築している Android デバイス ベンダーは、このセクションをスキップできます。
Android エコシステムを初めて利用する方や、Thread チップを Android と互換性のあるものにし、デバイス ベンダーをサポートするシリコン ベンダーの方は、引き続きご覧ください。
Android デベロッパー Codelab に沿って進める
Android 開発環境を初めてセットアップする場合は、https://source.android.com/docs/setup/start の Codelab を使用してください。この Codelab の最後では、ソースコードからシミュレートされた Cuttlefish デバイスをビルドして実行できるようになります。
Thread HAL サービスをビルドする
Cuttlefish でスレッドを試す
Cuttlefish は仮想 Android デバイスです。独自の HAL サービスの構築を開始する前に、Cuttlefish で Thread を試して、HAL の仕組みを理解することをおすすめします。
Cuttlefish にはデフォルトの Thread HAL サービスが用意されており、シミュレートされた Thread(802.15.4)無線との間で UDP ソケットを介してパケットを送受信するシミュレートされた RCP で実装されています。
Cuttlefish インスタンスには、「ThreadNetworkDemoApp」がプリインストールされています。そのアプリを開いて、Cuttlefish デバイスをデフォルトの Thread ネットワークに参加させます。
また、テスト用に Thread ネットワークを構成するための ot-ctl
コマンドライン ツールと ot-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 サービス
Thread HAL API とともに、デフォルトの HAL サービスが含まれています。デフォルトの 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 について
Tethering Mainline モジュールの Thread スタックと同様に、Cuttlefish のデフォルトの Thread 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
ハードウェア機能を定義します。これは、デバイスが Thread ハードウェアをサポートしていることを Android システムに認識させるために必要です。これを行わないと、Android Thread スタックは有効になりません。
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;
まとめる
これで、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 メインライン モジュール(実際には「Tethering」モジュールの一部)には、ベンダーが指定してスタックの動作をカスタマイズできるオーバーレイ可能な構成がいくつか用意されています。一覧については、config_thread.xml をご覧ください。
通常、デバイスを Thread ボーダー ルーターとして有効にするには、config_thread_border_router_default_enabled
を true
に設定し、config_thread_vendor_name
、config_thread_vendor_oui
、config_thread_model_name
をベンダーまたはプロダクトの値に変更する必要があります。これらの値は、Thread 境界ルーターによって常にアドバタイズされる _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 エコシステムで境界ルーターを利用する場合は、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
統合テストでは、デバイスで Thread Mainline コードがどのように機能するかについて、より高い品質保証が得られます。テストは、次のコマンドで実行できます。
atest ThreadNetworkIntegrationTests
リリース済みのテストスイートを使用して VTS/CTS/MTS テストを実行する方法については、以下をご覧ください。
- https://source.android.com/docs/core/tests/vts
- https://source.android.com/docs/compatibility/cts/run
- https://docs.partner.android.com/mainline/test/mts(このリンクにアクセスするにはパートナーである必要があります)
Thread デモアプリでテストする
Cuttlefish デバイスと同様に、Thread デモアプリをシステム イメージに追加できます。
# ThreadNetworkDemoApp for testing PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp
エンドユーザー向けのユーザービルドには含まれないため、デバッグ / eng バリアント(PRODUCT_PACKAGES_DEBUG
など)にのみ追加する必要があります。