适用于 Android 的 Thread 网络 SDK

Thread Network SDK 提供类似于 钥匙串,让您的 Android 应用可以将 Thread 网络凭据共享给 Google Play 服务。这样,您的应用就可以通过 而无需直接公开凭据和用户数据。

只需进行几次 API 调用,即可:

  1. 向 Google Play 服务请求首选 Thread 网络凭据。
  2. 设置新的边界路由器,并将您的 Thread 网络凭据添加到 Google Play 服务。
  3. 如果已有现场边界路由器,则可以检查您的边界 路由器在首选网络中并进行迁移(如有必要)。

需要考虑多个用户和开发者的历程。我们将介绍 本指南介绍了这些概念,以及其他主要功能和推荐用法。

主要术语和 API 概念

在开始之前,您最好先了解一下以下术语:

  • Thread Network Credentials:对 Thread TLV 进行编码的 Thread TLV 的二进制 blob 线程网络名称、网络密钥以及 用于加入给定 Thread 网络的 Thread 设备。

  • Preferred Thread 网络凭据:自动选择的 Thread 网络 可以使用 getPreferredCredentials API。

  • 边界代理 ID:线程边界路由器的 16 字节全局唯一 ID 设备。此 ID 由边界路由器供应商创建和管理。

  • “线程边界路由器设置”应用:这是可用于设置 新的 Thread 边界路由器设备,并将 Thread 网络凭据添加到 Google Play 服务。您的应用是所添加的 并有权访问这些凭据

许多 Thread Network API 返回 任务 异步完成的调用。您可以使用 addOnSuccessListeneraddOnFailureListener 来注册用于接收结果的回调。如需了解详情,请参阅 任务 文档。

凭据所有权和维护

添加 Thread 网络凭据的应用会成为 并且具有访问这些凭据的完整权限。如果您尝试 访问其他应用添加的凭据时,您会收到PERMISSION_DENIED 错误。

作为应用所有者,建议您将凭据存储在 Google 当线程边界路由器网络更新时,Play 服务将保持最新状态。这个 即在需要时添加凭据,并在出现边框时更新凭据 路由器的 Thread 网络凭据发生更改,并且在 线程边界路由器已被移除或恢复出厂设置。

边境代理发现

凭据必须与边境代理人 ID 一起保存。您需要确保 您的线程边界路由器设置应用能够确定边界代理 ID Thread 边界路由器的屏幕。

线程边界路由器必须使用 mDNS 来通告线程网络信息。 包括网络名称、扩展的 Pan ID 和边界代理 ID。通过 这些属性对应的 txt 值为 nnxpid, 。

对于配有 Google 边界路由器的网络,Google Play 服务会自动 获取 Google Thread 网络凭据以供使用。

将 SDK 集成到您的 Android 应用中

如需开始使用,请完成以下步骤:

  1. 请按照以下网址中提供的说明操作: 设置 Google Play 服务

  2. 将 Google Play 服务依赖项添加到您的 build.gradle 文件中:

    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 的调用 一个 Activity,提示用户允许网络请求。如果是网络 且凭据已存储在 Thread SDK 数字钥匙串中, 返回您的应用

请求凭据

如需提示用户提供首选凭据,请执行以下操作:

  1. 声明 ActivityLauncher

    private lateinit var preferredCredentialsLauncher: ActivityResultLauncher<IntentSenderRequest>
    
  2. 处理 activity 结果,以 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. 调用 preferredCredentials 并启动 activity:

    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 网络,则可以使用 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 连接到哪个边界路由器设备 网络所属的广告资源。

在此示例中,我们将根据边境代理 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}]") }
}

检测并迁移现场边界路由器

如果您当前有现场边界路由器,则可以使用 isPreferredCredentials,用于确定边界路由器是否属于 连接到首选网络此 API 不会提示 用户授予权限,并根据存储的内容检查边界路由器凭据 。

对于不匹配的项,isPreferredCredentails 返回 0;对于匹配项,返回 1 匹配,作为 Int 数据类型。您可以使用 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 服务 以便与其他供应商共享您可以创建一个ThreadNetworkCredential 来自原始线程活跃操作数据集 TLV 列表的实例:

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

边界代理的 Thread 网络凭据

边界路由器设备的唯一标识符。要使用 getCredentialsByBorderAgent API,首先,您需要创建一个 ThreadBorderAgent 对象并传递边境代理 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}]") }
}

Thread 网络凭据(按扩展的 Pan ID)

getPreferredCredentials 类似,您也可以提示用户 从边界路由器的 Extended Pan ID 获取凭据。通过 getCredentialsByExtendedPanId 会返回 IntentSender,而 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}]") }
}

移除凭据

将边界路由器设备从住宅中移除或恢复出厂设置后,您 需要从 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 参考文档