Thread Network SDK for Android

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

只需呼叫幾項 API 呼叫,即可:

  1. 向 Google Play 服務要求偏好的 Thread 網路憑證。
  2. 設定新的邊界路由器,並將 Thread 網路憑證新增至 Google Play 服務。
  3. 如果您已經有欄位邊界路由器,請檢查邊界路由器是否位於偏好的網路,並視需要進行遷移。

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

重要詞彙與 API 概念

開始之前,建議您先瞭解下列條款:

  • Thread 網路憑證:執行緒 TLV 的二進位 blob,能將 Thread 網路名稱、網路金鑰及其他 Thread 裝置要求的其他屬性編碼,以便加入指定 Thread 網路。

  • 偏好的執行緒網路憑證:自動選取的 Thread 網路憑證,可能會透過 getPreferredCredentials API 分享給不同供應商的應用程式。

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

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

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

憑證擁有權與維護

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

我們建議執行緒擁有者更新 Thread 邊界路由器網路時,將憑證儲存至 Google Play 服務。這表示在必要時新增憑證、在邊界路由器的 Thread 網路憑證變更時更新憑證,以及在移除執行緒邊界路由器或恢復原廠設定時移除憑證。

使用邊界代理程式探索功能

憑證必須使用邊界代理程式 ID 儲存。您必須確保 Thread 邊界路由器設定應用程式能夠決定 Thread 邊界路由器的邊界代理程式 ID。

Thread 邊界路由器必須使用 mDNS 通告執行緒網路資訊,包括網路名稱、擴充平移 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 會啟動活動,提示使用者允許網路要求。如果網路憑證已儲存在 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 並啟動活動:

    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 網路憑證新增至 Google Play 服務,才能讓其他 Thread 供應商使用。在您新增憑證之前,還需要知道 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 不相符時會傳回 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 服務,以便與其他廠商共用。您可以使用原始的執行緒作業資料集 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}]") }
    

邊界 API 代理程式的執行緒網路憑證

邊界代理程式 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 類似,您也可以提示使用者從邊界路由器的 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}]") }
}

移除憑證

從住家或恢復原廠設定移除 Border 路由器裝置後,你必須從 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 網路 SDK,請參閱 API 參考資料