Thread Network SDK 的功能類似數位鑰匙圈,可讓 Android 應用程式與 Google Play 服務共用 Thread 網路憑證。這樣一來,應用程式就能設定任何智慧住宅生態系統中的 Thread 裝置,不必直接公開憑證和使用者資料。
只要呼叫幾個 API,就能:
- 向 Google Play 服務要求偏好的 Thread 網路憑證。
- 設定新的 Thread Border Router (TBR),並將 Thread 網路憑證新增至 Google Play 服務。
- 如果您已有現場 TBR,可以檢查 TBR 是否位於偏好網路,並視需要遷移。
有幾種使用者和開發人員歷程需要考量。本指南將介紹其中大部分功能,以及其他重要功能和建議用法。
重要術語和 API 概念
開始之前,建議您先瞭解下列詞彙:
Thread 網路憑證:Thread TLV 的二進位 blob,用於編碼 Thread 網路名稱、網路金鑰和其他屬性,Thread 裝置必須具備這些屬性,才能加入特定 Thread 網路。
偏好的 Thread 網路憑證:自動選取的 Thread 網路憑證,可使用
getPreferredCredentialsAPI 與不同供應商的應用程式共用。邊界代理 ID:TBR 裝置的 16 位元組全域專屬 ID。這個 ID 是由 border router 個供應商建立及管理。
TBR 設定應用程式:這是 Android 應用程式,可設定新的 TBR 裝置,並將 Thread 網路憑證新增至 Google Play 服務。應用程式是新增憑證的授權擁有者,且可存取這些憑證。
許多 Thread Network API 會傳回非同步完成的工作。您可以使用 addOnSuccessListener 和 addOnFailureListener 註冊回呼,以便接收結果。詳情請參閱工作說明文件。
憑證擁有權和維護
新增 Thread 網路憑證的應用程式會成為憑證擁有者,並具備存取憑證的完整權限。如果嘗試存取其他應用程式新增的憑證,系統會傳回 PERMISSION_DENIED 錯誤。
建議應用程式擁有者在 TBR 網路更新時,一併更新儲存在 Google Play 服務中的憑證。這表示在必要時新增憑證、在 border router 的 Thread 網路憑證變更時更新憑證,以及在 TBR 移除或恢復原廠設定時移除憑證。
探索邊界代理
儲存憑證時,必須一併提供邊境人員 ID。請務必確認 TBR 設定應用程式能夠判斷 TBR 的邊界代理程式 ID。
TBR 必須使用 mDNS 宣傳 Thread 網路資訊,包括網路名稱、擴充 PAN ID 和邊界代理程式 ID。這些屬性對應的 txt 值分別為 nn、xp 和 id。
如果是 Google Thread Border Router (gTBR) 網路,Google Play 服務會自動取得 Google Thread 網路憑證。
將 SDK 整合至 Android 應用程式
如要開始使用,請完成下列步驟:
按照「設定 Google Play 服務」一文的指示操作。
在
build.gradle檔案中新增 Google Play 服務依附元件:implementation 'com.google.android.gms:play-services-threadnetwork:16.2.1'選用:定義
BorderAgent資料類別,用於儲存 TBR 資訊。本指南將使用這項資料:data class BorderAgentInfo( // Network Name max 16 len val networkName: String = "", val extPanId: ByteArray = ByteArray(16), val borderAgentId: ByteArray = ByteArray(16), ... )
接下來,我們將說明新增及管理偏好憑證的建議步驟。
新的 Thread 邊界路由器設定
為新的邊界路由器建立新網路前,請務必先嘗試使用偏好的網路憑證。這可確保 Thread 裝置盡可能連線至單一 Thread 網路。
呼叫 getPreferredCredentials 會啟動 Activity,提示使用者允許網路要求。如果網路憑證已儲存在 Thread SDK 數位鑰匙圈中,系統會將憑證傳回應用程式。
要求憑證
如要提示使用者提供偏好的憑證,請按照下列步驟操作:
宣告
ActivityLauncher:private lateinit var preferredCredentialsLauncher: ActivityResultLauncher<IntentSenderRequest>處理 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.") } }如果您要設定新的 TBR,建議您呼叫
preferredCredentials並啟動活動。這項呼叫可確保新的 TBR 使用與手機中儲存為「偏好」相同的憑證,進而將不同的 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}]") } }如果您的用途與設定非 TBR 裝置有關,例如新的 Matter-over-Thread 終端裝置,建議您使用
allActiveCredentialsAPI 擷取憑證。這項呼叫會掃描區域網路中找到的 TBR,因此不會傳回本機現有 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 網路憑證,也沒有可用的有效 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 網路屬於哪個 TBR 裝置。
在本例中,我們將從邊界代理 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}]") }
}
偵測及遷移現場的 border router
如果您有現場 border router,可以使用 isPreferredCredentials 判斷 border router 是否屬於偏好網路。這個 API 不會提示使用者授予權限,而是會根據 Google Play 服務中儲存的憑證檢查 border router 憑證。
isPreferredCredentials 會傳回 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 網路憑證 (依作業資料集)
在某些情況下,您的 TBR 已設定 Thread 網路,而您想將這個 Thread 網路新增至 Google Play 服務,以便與其他供應商共用。您可以從原始 Thread Active Operational Dataset TLV 清單建立 ThreadNetworkCredential 執行個體:
將作業資料集轉換為
ByteArray。例如:val activeDataset = "0e080000000000010000000300000f35060004001fffe0020833333333...".dsToByteArray()fun String.dsToByteArray(): ByteArray { return chunked(2).map { it.toInt(16).toByte() }.toByteArray() }使用
fromActiveOperationalDataset建立ThreadNetworkCredentials。成功後,您就能取得 Thread 網路名稱、頻道和其他網路資訊。如需完整的屬性清單,請參閱 ThreadNetworkCredentials。val threadNetworkCredentials = ThreadNetworkCredentials.fromActiveOperationalDataset(activeDataset) Log.d( "threadNetworkCredentials", threadNetworkCredentials.channel.toString() + " - " + threadNetworkCredentials.networkName)呼叫
isPreferredCredentialsAPI 並傳遞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 網路憑證
邊界代理 ID 可用來識別 TBR 裝置。如要使用 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 類似,您也可以提示使用者提供 TBR 擴充 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 router 裝置或恢復原廠設定時,必須從 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 參考資料。