Android 向け Thread ネットワーク SDK

Thread Network SDK は、デジタル キーチェーンに似た機能を提供し、Android アプリが Google Play 開発者サービスと Thread ネットワーク認証情報を共有できるようにします。これにより、アプリは認証情報やユーザーデータを直接公開することなく、任意のスマートホーム エコシステムから任意の Thread デバイスをセットアップできます。

API 呼び出しを数回行うだけで、次の操作を行えます。

  1. Google Play 開発者サービスから優先 Thread ネットワーク認証情報をリクエストします。
  2. 新しい Thread Border Router (TBR) を設定し、Thread ネットワークの認証情報を Google Play 開発者サービスに追加します。
  3. すでにフィールドに TBR がある場合は、TBR が優先ネットワークにあるかどうかを確認し、必要に応じて移行できます。

考慮すべきユーザー ジャーニーとデベロッパー ジャーニーがいくつかあります。このガイドでは、これらのほとんどの機能と、その他の重要な機能、推奨される使用方法について説明します。

主な用語と API のコンセプト

始める前に、次の用語を理解しておくと役立ちます。

  • Thread ネットワーク認証情報: 特定の Thread ネットワークに接続するために Thread デバイスが必要とする Thread ネットワーク名、ネットワーク キー、その他のプロパティをエンコードする Thread TLV のバイナリ BLOB。

  • 優先 Thread ネットワーク認証情報: getPreferredCredentials API を使用して、異なるベンダーのアプリと共有できる、自動選択された Thread ネットワーク認証情報。

  • Border Agent ID: TBR デバイスの 16 バイトのグローバルに一意の ID。この ID は border router ベンダーによって作成、管理されます。

  • TBR セットアップ アプリ: 新しい TBR デバイスをセットアップし、Thread ネットワーク認証情報を Google Play 開発者サービスに追加する Android アプリです。アプリは追加された認証情報の権限のある所有者であり、認証情報にアクセスできます。

Thread ネットワーク API の多くは、非同期で完了する Task を返します。addOnSuccessListeneraddOnFailureListener を使用して、結果を受け取るためのコールバックを登録できます。詳細については、タスクのドキュメントをご覧ください。

認証情報の所有権とメンテナンス

Thread ネットワーク認証情報を追加するアプリが認証情報のオーナーになり、認証情報への完全なアクセス権が付与されます。他のアプリによって追加された認証情報にアクセスしようとすると、PERMISSION_DENIED エラーが発生します。

アプリの所有者は、TBR ネットワークが更新されたら、Google Play 開発者サービスに保存されている認証情報を最新の状態に保つことをおすすめします。つまり、必要に応じて認証情報を追加し、border router の Thread ネットワーク認証情報が変更されたときに認証情報を更新し、TBR が削除または出荷時の設定にリセットされたときに認証情報を削除します。

Border Agent の検出

認証情報は Border Agent ID とともに保存する必要があります。TBR の Border Agent ID を TBR のセットアップ アプリが特定できるようにする必要があります。

TBR は、mDNS を使用して、ネットワーク名、拡張 Pan ID、ボーダー エージェント ID などの Thread ネットワーク情報をアドバタイズする必要があります。これらの属性に対応する txt の値は、それぞれ nnxpid です。

Google Thread Border Router (gTBR) を含むネットワークの場合、Google Play 開発者サービスは使用する Google Thread ネットワーク認証情報を自動的に取得します。

SDK を Android アプリに統合する

開始するには、次の手順を完了します。

  1. Google Play 開発者サービスを設定するの手順に沿って操作します。

  2. build.gradle ファイルに Google Play 開発者サービスの依存関係を追加します。

    implementation 'com.google.android.gms:play-services-threadnetwork:16.2.1'
    
  3. 省略可: TBR 情報を保存する BorderAgent データクラスを定義します。このガイドでは、このデータを全体で使用します。

    data class BorderAgentInfo(
      // Network Name max 16 len
      val networkName: String = "",
      val extPanId: ByteArray = ByteArray(16),
      val borderAgentId: ByteArray = ByteArray(16),
      ...
    )
    

次に、優先認証情報を追加して管理するための推奨手順について説明します。

新しい Thread ボーダー ルーターのセットアップ

新しいボーダー ルーター用に新しいネットワークを作成する前に、優先ネットワークの認証情報を試すことが重要です。これにより、Thread デバイスは可能な限り単一の Thread ネットワークに接続されます。

getPreferredCredentials を呼び出すと、アクティビティが起動し、ネットワーク リクエストを許可するようユーザーに求められます。ネットワーク認証情報が Thread SDK のデジタル キーチェーンに保存されている場合、認証情報はアプリに返されます。

認証情報をリクエストする

優先する認証情報をユーザーに求めるには:

  1. ActivityLauncher を宣言します。

    private lateinit var preferredCredentialsLauncher: ActivityResultLauncher<IntentSenderRequest>
    
  2. ThreadNetworkCredentials として返されるアクティビティの結果を処理します。

    preferredCredentialsLauncher =
     registerForActivityResult(
       StartIntentSenderForResult()
     ) { result: ActivityResult ->
       if (result.resultCode == RESULT_OK) {
         val threadNetworkCredentials = ThreadNetworkCredentials.fromIntentSenderResultData(result.data!!)
         Log.d("debug", threadNetworkCredentials.networkName)
       } else {
         Log.d("debug", "User denied request.")
       }
     }
    
  3. 新しい TBR を設定する場合は、preferredCredentials を呼び出してアクティビティを起動することをおすすめします。この呼び出しにより、新しい TBR は、スマートフォンに優先としてすでに保存されている同じ認証情報を使用し、異なる TBR を同じネットワークに統合します。

    private fun getPreferredThreadNetworkCredentials() {
      ThreadNetwork.getClient(this)
        .preferredCredentials
      .addOnSuccessListener { intentSenderResult ->
        intentSenderResult.intentSender?.let {
          preferredCredentialsLauncher.launch(IntentSenderRequest.Builder(it).build())
          } ?: Log.d("debug", "No preferred credentials found.")
        }
      .addOnFailureListener { e: Exception -> Log.d(TAG, "ERROR: [${e}]") }
    }
    
  4. 新しい Matter-over-Thread エンドデバイスなど、TBR 以外のデバイスのセットアップに関連するユースケースの場合は、allActiveCredentials API を使用して認証情報を取得することをおすすめします。この呼び出しはローカル ネットワークで見つかった TBR をスキャンするため、ローカルに存在する TBR で利用できない認証情報は返されません。

    // Creates the IntentSender result launcher for the getAllActiveCredentials API
    private val getAllActiveCredentialsLauncher =
      registerForActivityResult(
        StartIntentSenderForResult()
      ) { result: ActivityResult ->
        if (result.resultCode == RESULT_OK) {
          val activeCredentials: List<ThreadNetworkCredentials> =
            ThreadNetworkCredentials.parseListFromIntentSenderResultData(
              result.data!!
            )
          // Use the activeCredentials list
        } else {
          // The user denied to share!
        }
      }
    
    // Invokes the getAllActiveCredentials API and starts the dialog activity with the returned
    // IntentSender
    threadNetworkClient
    .getAllActiveCredentials()
    .addOnSuccessListener { intentSenderResult: IntentSenderResult ->
      val intentSender = intentSenderResult.intentSender
      if (intentSender != null) {
        getAllActiveCredentialsLauncher.launch(
          IntentSenderRequest.Builder(intentSender).build()
        )
      } else {
        // No active network credentials found!
      }
    }
    // Handles the failure
    .addOnFailureListener { e: Exception ->
      // Handle the exception
    }
    

新しい Thread ネットワークを作成する

ユーザーの Thread ネットワークで優先 Thread ネットワーク認証情報もアクティブな Thread 認証情報も利用できない場合は、addCredentials API を使用して Google Play 開発者サービスに認証情報を追加できます。これを行うには、ThreadBorderAgent を作成し、ThreadNetworkCredentials オブジェクトも指定する必要があります。

ランダム ネットワークを作成するには、newRandomizeBuilder を呼び出します。

val threadCredentials = ThreadNetworkCredentials.newRandomizedBuilder().build()

Thread ネットワーク名を指定するには:

val threadCredentials = ThreadNetworkCredentials.newRandomizedBuilder()
  .setNetworkName("ThreadNetworkSDK")
  .build()

認証情報を追加

他の Thread ベンダーが Thread ネットワークの認証情報を使用できるようにするには、Google Play 開発者サービスに認証情報を追加する必要があります。新しい認証情報を追加する前に、この Thread ネットワークがどの TBR デバイスに属しているかを知る必要があります。

この例では、ボーダー エージェント ID から ThreadBorderAgent を作成し、作成したばかりの新しい Thread ネットワーク認証情報を渡します。

private fun addCredentials(borderAgentInfo: BorderAgentInfo, credentialsToBeAdded: ThreadNetworkCredentials) {

  val threadBorderAgent = ThreadBorderAgent.newBuilder(borderAgentInfo.borderAgentId).build()
  Log.d("debug", "border router id:" + threadBorderAgent.id)

  ThreadNetwork.getClient(this)
    .addCredentials(threadBorderAgent, credentialsToBeAdded)
      .addOnSuccessListener {
        Log.d("debug", "Credentials added.")
      }
      .addOnFailureListener { e: Exception -> Log.d(TAG, "ERROR: [${e}]") }
}

フィールド内の border router を検出して移行する

フィールド内に border router がある場合は、isPreferredCredentials を使用して、border router が優先ネットワークに属しているかどうかを判断できます。この API はユーザーに権限を求めることはなく、Google Play 開発者サービスに保存されている情報と照合して border router 認証情報を確認します。

isPreferredCredentials は、一致しない場合は 0 を、一致する場合は 1Int データ型として返します。IsPreferredCredentialsResult を使用して結果を確認できます。

public @interface IsPreferredCredentialsResult {
    int PREFERRED_CREDENTIALS_NOT_FOUND = -1;
    int PREFERRED_CREDENTIALS_NOT_MATCHED = 0;
    int PREFERRED_CREDENTIALS_MATCHED = 1;
}

isPreferredCredentials を使用するには、まず ThreadNetworkCredentials オブジェクトを作成する必要があります。ThreadNetworkCredentials をインスタンス化する方法はいくつかあります。次の手順では、これらのオプションについて説明します。

運用データセット別の Thread ネットワーク認証情報

TBR が Thread ネットワークで設定済みで、この Thread ネットワークを Google Play 開発者サービスに追加して他のベンダーと共有したい場合もあります。未加工の Thread Active Operational Dataset TLV リストから ThreadNetworkCredential インスタンスを作成できます。

  1. 運用データセットを ByteArray に変換します。次に例を示します。

    val activeDataset =
          "0e080000000000010000000300000f35060004001fffe0020833333333...".dsToByteArray()
    
    fun String.dsToByteArray(): ByteArray {
      return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
    }
    
  2. fromActiveOperationalDataset を使用して ThreadNetworkCredentials を作成します。成功すると、Thread ネットワークの名前、チャンネル、その他のネットワーク情報を取得できます。プロパティの完全なリストについては、ThreadNetworkCredentials をご覧ください。

    val threadNetworkCredentials =
        ThreadNetworkCredentials.fromActiveOperationalDataset(activeDataset)
    Log.d(
        "threadNetworkCredentials",
        threadNetworkCredentials.channel.toString() + " - " + threadNetworkCredentials.networkName)
    
  3. isPreferredCredentials API を呼び出し、ThreadNetworkCredentials を渡します。

    ThreadNetwork.getClient(this)
    .isPreferredCredentials(threadNetworkCredentials)
    .addOnSuccessListener { result ->
      when (result) {
        IsPreferredCredentialsResult.PREFERRED_CREDENTIALS_NOT_MATCHED ->
            Log.d("isPreferredCredentials", "Credentials not matched.")
        IsPreferredCredentialsResult.PREFERRED_CREDENTIALS_MATCHED ->
            Log.d("isPreferredCredentials", "Credentials matched.")
      }
    }
    .addOnFailureListener { e: Exception -> Log.d("isPreferredCredentials", "ERROR: [${e}]") }
    

Border Agent による Thread ネットワーク認証情報

ボーダー エージェント ID は TBR デバイスを一意に識別します。getCredentialsByBorderAgent API を使用するには、まず ThreadBorderAgent オブジェクトを作成して Border Agent ID を渡す必要があります。

ThreadBorderAgent オブジェクトを作成したら、getCredentialsByBorderAgent を呼び出します。認証情報が保存されている場合は、優先されるかどうかを確認します。

private fun isPreferredThreadNetworkByBorderAgent(borderAgentInfo: BorderAgentInfo) {

  val threadBorderAgent = ThreadBorderAgent.newBuilder(borderAgentInfo.borderAgentId).build()
  Log.d("debug", "border router id:" + threadBorderAgent.id)

  var isPreferred = IsPreferredCredentialsResult.PREFERRED_CREDENTIALS_NOT_FOUND
  var borderAgentCredentials: ThreadNetworkCredentials?
  val taskByBorderAgent = ThreadNetwork.getClient(this)
  taskByBorderAgent
      .getCredentialsByBorderAgent(threadBorderAgent)
      .addOnSuccessListener { result: ThreadNetworkCredentialsResult ->
        borderAgentCredentials = result.credentials
        result.credentials?.let {
          taskByBorderAgent.isPreferredCredentials(it).addOnSuccessListener { result ->
            isPreferred = result
          }
        }
      }
      .addOnFailureListener { e: Exception -> Log.d(TAG, "ERROR: [${e}]") }
}

拡張 PAN ID 別の Thread ネットワーク認証情報

getPreferredCredentials と同様に、TBR の拡張 PAN ID からユーザーに認証情報を求めることもできます。ユーザーが承認すると、getCredentialsByExtendedPanIdIntentSender を返し、アクティビティの結果に ThreadNetworkCredentials オブジェクトが含まれます。

private fun getCredentialsByExtPanId(borderAgentInfo: BorderAgentInfo) {
  ThreadNetwork.getClient(this)
    .getCredentialsByExtendedPanId(borderAgentInfo.extPanId)
    .addOnSuccessListener { intentSenderResult ->
      intentSenderResult.intentSender?.let {
        preferredCredentialsLauncher.launch(IntentSenderRequest.Builder(it).build())
      }
        ?: Log.d("debug", "No credentials found.")
    }
    .addOnFailureListener { e: Exception -> Log.d(TAG, "ERROR: [${e}]") }
}

認証情報を削除する

border router デバイスが家から削除された場合や、出荷時の設定にリセットされた場合は、Google Play 開発者サービスから Thread ネットワークを削除する必要があります。

private fun removeCredentials(borderAgentInfo: BorderAgentInfo) {

  val threadBorderAgent = ThreadBorderAgent.newBuilder(borderAgentInfo.borderAgentId).build()
  Log.d("debug", "border router id:" + threadBorderAgent.id)

  ThreadNetwork.getClient(this)
      .removeCredentials(threadBorderAgent)
      .addOnSuccessListener { Log.d("debug", "Credentials removed.") }
      .addOnFailureListener { e: Exception -> Log.d(TAG, "ERROR: [${e}]") }
}

リソース

Thread Network SDK の詳細については、API リファレンスをご覧ください。