Thread Network SDK for Android

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

いくつかの API 呼び出しで、次のことができます。

  1. 優先 Thread ネットワーク認証情報を Google Play 開発者サービスにリクエストします。
  2. 新しいボーダー ルーターを設定し、Thread ネットワーク認証情報を Google Play 開発者サービスに追加します。
  3. 現場にボーダー ルーターがすでにある場合は、ボーダー ルーターが優先ネットワークにあるかどうかを確認し、必要に応じて移行します。

ユーザーとデベロッパーのジャーニーがいくつか検討されています。このガイドでは、ほとんどの機能と、その他の主な機能や推奨使い方について説明します。

16.0.0

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

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

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

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

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

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

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

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

Thread ネットワーク認証情報を追加するアプリが認証情報の所有者となり、認証情報にアクセスするための完全な権限を持つ。他のアプリによって追加された認証情報にアクセスしようとすると、PERMISSION_DENIED エラーが発生します。

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

ボーダー エージェントの検出

認証情報は Border Agent ID を使用して保存する必要があります。Thread ボーダー ルーターのセットアップ アプリで、Thread ボーダー ルーターのボーダー エージェント ID を特定できることを確認する必要があります。

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

Google ボーダー ルーターを使用するネットワークの場合、Google Play 開発者サービスは、使用するための Google Thread ネットワーク認証情報を自動的に取得します。

SDK を Android アプリに統合する

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

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

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

    implementation 'com.google.android.gms:play-services-threadnetwork:16.0.0'
    
  3. 省略可: ボーダー ルーター情報を格納する BorderAgent データクラスを定義します。このガイドは、このガイド全体を通して使用します。

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

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

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

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

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

認証情報をリクエスト

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

  1. ActivityLauncher を宣言します。

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

    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. preferredCredentials を呼び出して、アクティビティを起動します。

    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}]") }
    }
    

新しい 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 ネットワークが属するボーダー ルーター デバイスを把握している必要があります。

この例では、Border Agent ID から ThreadBorderAgent を作成し、作成した新しい Thread ネットワーク認証情報を渡します。

現場のボーダー ルーターの検出と移行

現場にボーダー ルーターがある場合は、isPreferredCredentials を使用して、ボーダー ルーターが優先ネットワークに属しているかどうかを判断できます。この API は、ユーザーに権限を要求せず、ボーダー ルーターの認証情報を Google Play 開発者サービスに保存されているものと照合します。

isPreferredCredentails は、一致しない場合は 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 ボーダー ルーターがすでに Thread ネットワークで設定されていて、この Thread ネットワークを Google Play 開発者サービスに追加して、他のベンダーと共有したい場合があります。未加工のスレッドのアクティブなオペレーショナル データセット 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 は、ボーダー ルーター デバイスを一意に識別します。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}]") }
}

拡張パン ID によるスレッド ネットワーク認証情報

getPreferredCredentials と同様に、ボーダー ルーターの拡張パン ID からの認証情報をユーザーに求めることもできます。getCredentialsByExtendedPanIdIntentSender を返し、ユーザーが承認すると Activity の結果に 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}]") }
}

認証情報を削除する

ボーダー ルーター デバイスを家や工場からリセットした場合、その Thread ネットワークを Google Play 開発者サービスから削除する必要があります。

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 リファレンスをご覧ください。