Android 適用的 Thread Network SDK

Thread Network SDK 提供類似於數位鑰匙圈的功能,可讓 Android 應用程式與 Google Play 服務共用 Thread 網路憑證。這樣一來,應用程式就能在以下位置設定任何 Thread 裝置: 任何智慧型住宅生態系統,而不必直接揭露憑證和使用者資料。

只要發出幾個 API 呼叫,您就能:

  1. 向 Google Play 服務要求 Thread 網路憑證。
  2. 設定新的邊界路由器,並將 Thread 網路憑證新增至 Google Play 服務。
  3. 如果已有內邊界路由器,可以按照下列步驟查看邊界 且路由器位於偏好的網路中,並視需要遷移。

這裡有幾個使用者和開發人員歷程需要列入考量。這部影片將介紹 。

重要術語和 API 概念

在開始之前,建議您先瞭解下列字詞:

  • Thread Network Credentials:編碼的 Thread TLV 二進位檔 blob 執行緒網路名稱、網路金鑰和其他屬性 用來加入指定 Thread 網路的 Thread 裝置。

  • 偏好的 Thread 網路憑證:自動選取的 Thread 網路 憑證能分享給不同供應商的應用程式 getPreferredCredentials API。

  • 邊界代理程式 ID:Thread 邊界路由器的 16 位元組全域專屬 ID 裝置。這個 ID 是由邊界路由器供應商建立及管理。

  • Thread 邊界路由器設定應用程式:這是要設定 新的 Thread 邊界路由器裝置,並將 Thread 網路憑證新增至 Google Play 服務。您的應用程式是新增憑證的權威擁有者,並可存取這些憑證。

許多 Thread Network API 會傳回以非同步方式完成的 Task。別擔心!您可以使用 addOnSuccessListeneraddOnFailureListener 註冊回呼以接收結果。詳情請參閱 工作 說明文件。

憑證擁有權和維護

新增 Thread 網路憑證的應用程式會成為憑證的擁有者,並擁有存取憑證的完整權限。如果您嘗試 如要存取其他應用程式新增的憑證,將會收到 PERMISSION_DENIED 錯誤。

身為應用程式擁有者,建議您在 Thread Border Router 網路更新時,將 Google Play 服務中儲存的憑證保持在最新狀態。也就是說,您需要在必要時新增憑證、在邊界路由器的 Thread 網路憑證變更時更新憑證,以及在移除 Thread 邊界路由器或將其重設為原廠設定時移除憑證。

邊境服務專員探索

憑證必須以邊框代理商 ID 儲存。請確認 Thread 邊界路由器設定應用程式能夠判斷 Thread 邊界路由器的邊界代理程式 ID。

Thread 邊界路由器必須使用 mDNS 宣傳 Thread 網路資訊,包括網路名稱、擴充 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.2.1'
    
  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 的形式傳回:

    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. 如果您要設定新的 Thread Border Router,建議您呼叫 preferredCredentials 並啟動 Activity。這項呼叫可確保 新的 Thread 邊界路由器將使用先前儲存的相同憑證 是電話上首選,宣傳不同 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. 如果您的用途與設定非 TBR 裝置有關,例如新的 使用 Matter 呼叫端裝置時,建議您使用 allActiveCredentials 用於擷取憑證的 API。這項呼叫會掃描在 因此不會傳回由網路管理員 無法使用的憑證 現有的 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 網路之後,就可以使用 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 不會提示 要求使用者授予權限,並依據儲存的內容檢查邊界路由器憑證 以及 Google Play 服務

isPreferredCredentails 會以 Int 資料類型傳回 0 (不相符) 或 1 (相符)。您可以使用「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}]") }
    

由 Border Agent 分組網路憑證

邊界代理程式 ID 專門用來識別邊界路由器裝置。如要使用 getCredentialsByBorderAgent API,您必須先建立 ThreadBorderAgent 物件,並傳遞邊境管理員 ID。

建立 ThreadBorderAgent 物件後,請呼叫 getCredentialsByBorderAgent。如果憑證已儲存,請檢查 在 Kubernetes 中

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

依 Extended Pan ID 排序的 Thread 網路憑證

getPreferredCredentials 類似,您也可以提示使用者 來自邊界路由器的擴充平移 IDgetCredentialsByExtendedPanId 會傳回 IntentSender,而活動結果會在使用者核准時包含 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 參考資料