Thread Network SDK for Android

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

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

只需幾個 API 呼叫,您就可以:

  1. 向 Google Play 服務要求偏好的 Thread 網路憑證。
  2. 設定新的邊界路由器,並將 Thread 網路憑證新增至 Google Play 服務。
  3. 如果您已有現場邊界路由器,則可查看邊界路由器是否在偏好的網路中,並在需要時進行遷移。

需要考量多個開發人員和開發人員的流程。本指南將介紹大部分內容,以及其他重要功能和建議使用方式。

重要術語及 API 概念

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

  • Thread 網路憑證:執行緒 TLV 的二進位 blob,將執行緒網路名稱、網路金鑰和其他屬性所需的編碼,以納入執行緒系統所需的特定執行緒。

  • 偏好的 Thread 網路憑證:自動選取的 Thread 網路憑證,可透過 getPreferredCredentials API 與不同供應商的應用程式共用。

  • 邊界代理程式 ID:為 Thread Border 路由器裝置的全域唯一 ID。由邊界路由器廠商建立及管理。

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

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

憑證擁有權和維護

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

身為應用程式擁有者,我們建議您在更新 Thread 邊界路由器網路時,將 Google Play 服務中儲存的憑證保持在最新狀態。這表示在需要時新增憑證,在邊界路由器的 Thread 網路憑證變更時更新憑證,並在移除執行緒邊界或恢復原廠設定時移除憑證。

探索代理程式代理程式

憑證必須連同邊界代理程式 ID 儲存。您必須確認 Thread Border 路由器設定應用程式能夠判斷執行緒邊框路由器的邊界代理程式 ID。

執行緒邊界路由器必須使用 mDNS 來公告 Thread 網路資訊,包括網路名稱、擴充窗格 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 裝置盡可能連線至單一執行緒網路。

呼叫 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. 呼叫 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 網路中偏好的 Thread 網路憑證,您可以使用 addCredentials API 將憑證新增至 Google Play 服務。如要這麼做,您必須建立 ThreadBorderAgent,並提供 ThreadNetworkCredentials 物件。

如要建立隨機網路,請呼叫 newRandomizeBuilder

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

如何指定執行緒網路名稱:

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 會傳回未比對的 01 則表示比對為 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 網路進行設定,而您想將這個執行緒網路新增到 Google Play 服務,然後與其他供應商共用。您可以從原始的 Thread 運作運作資料集 TLV 清單建立 ThreadNetworkCredential 執行個體:

  1. 將作業資料集轉換為 ByteArray。例如:

    val activeDataset =
          "0e080000000000010000000300000f35060004001fffe0020833333333...".dsToByteArray()
    
    fun String.dsToByteArray(): ByteArray {
      return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
    }
    
  2. 使用 fromActiveOperationalDataset 建立 ThreadNetworkCredentials。成功後,您就能取得執行緒網路名稱、頻道和其他網路資訊。如需完整的屬性清單,請參閱 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 代理程式的執行緒網路憑證

邊界代理程式 ID 可明確識別邊界路由器裝置。如要使用 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}]") }
}

按 Extended Pan ID 劃分的執行緒網路憑證

getPreferredCredentials 類似,您也可以提示使用者從邊框路由器的擴充擴充功能 ID 要求憑證。getCredentialsByExtendedPanId 會傳回 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}]") }
}

移除憑證

如果您的 Border 路由器裝置從住家或恢復原廠設定後遭到移除,就必須從 Google Play 服務中移除 BThread 網路。

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 參考資料