SDK mạng Thread dành cho Android

Thread Network SDK cung cấp chức năng tương tự như chuỗi khoá kỹ thuật số, cho phép các ứng dụng Android chia sẻ thông tin xác thực mạng Thread với Dịch vụ Google Play. Điều này cho phép ứng dụng của bạn thiết lập mọi thiết bị Thread từ bất kỳ hệ sinh thái nhà thông minh nào mà không trực tiếp làm lộ thông tin xác thực và dữ liệu người dùng.

Chỉ với một vài lệnh gọi API, bạn có thể:

  1. Yêu cầu thông tin đăng nhập mạng Thread ưu tiên từ Dịch vụ Google Play.
  2. Thiết lập Thread Border Router (TBR) mới và thêm thông tin đăng nhập mạng Thread vào Dịch vụ Google Play.
  3. Nếu đã có TBR tại chỗ, bạn có thể kiểm tra xem TBR của mình có nằm trong mạng ưu tiên hay không và di chuyển các TBR đó nếu cần.

Có một số hành trình của người dùng và nhà phát triển cần xem xét. Chúng tôi sẽ đề cập đến hầu hết các tính năng đó trong hướng dẫn này, cùng với các tính năng chính khác và cách sử dụng được đề xuất.

Thuật ngữ chính và khái niệm về API

Trước khi bắt đầu, bạn nên tìm hiểu các thuật ngữ sau:

  • Thông tin đăng nhập mạng Thread: Blob nhị phân của TLV Thread mã hoá Tên mạng Thread, Khoá mạng và các thuộc tính khác mà thiết bị Thread cần để tham gia một mạng Thread nhất định.

  • Thông tin đăng nhập mạng Thread ưu tiên: Thông tin đăng nhập mạng Thread được chọn tự động có thể được chia sẻ với các ứng dụng của nhiều nhà cung cấp bằng API getPreferredCredentials.

  • Mã nhận dạng Border Agent: Mã nhận dạng duy nhất trên toàn cầu gồm 16 byte cho một thiết bị TBR. Mã này do các nhà cung cấp border router tạo và quản lý.

  • Ứng dụng thiết lập TBR: Đây là ứng dụng Android thiết lập các thiết bị TBR mới và thêm thông tin đăng nhập mạng Thread vào Dịch vụ Google Play. Ứng dụng của bạn là chủ sở hữu có thẩm quyền đối với thông tin đăng nhập đã thêm và có quyền truy cập vào thông tin đăng nhập đó.

Nhiều API Thread Network trả về một Task hoàn tất không đồng bộ. Bạn có thể sử dụng addOnSuccessListener và addOnFailureListener để đăng ký lệnh gọi lại nhằm nhận kết quả. Để tìm hiểu thêm, hãy tham khảo tài liệu về Task.

Quyền sở hữu và duy trì thông tin đăng nhập

Ứng dụng thêm thông tin xác thực mạng Thread sẽ trở thành chủ sở hữu của thông tin xác thực và có toàn quyền truy cập vào thông tin xác thực. Nếu cố gắng truy cập vào thông tin xác thực do các ứng dụng khác thêm, bạn sẽ nhận được lỗi PERMISSION_DENIED.

Là chủ sở hữu ứng dụng, bạn nên cập nhật vùng lưu trữ thông tin xác thực được lưu trữ trong Dịch vụ Google Play khi mạng TBR được cập nhật. Điều này có nghĩa là thêm thông tin đăng nhập khi cần, cập nhật thông tin đăng nhập khi thông tin đăng nhập mạng Thread của border router thay đổi và xoá thông tin đăng nhập khi TBR bị xoá hoặc đặt lại về trạng thái ban đầu.

Khám phá Border Agent

Bạn phải lưu thông tin xác thực cùng với mã nhân viên biên phòng. Bạn cần đảm bảo rằng ứng dụng thiết lập TBR có thể xác định mã nhận dạng Border Agent của TBR.

TBR phải sử dụng mDNS để quảng cáo thông tin mạng Thread, bao gồm Tên mạng, Mã PAN mở rộng và Mã tác nhân biên. Các giá trị txt tương ứng cho các thuộc tính này lần lượt là nn, xp và id.

Đối với các mạng có Google Thread Border Router (gTBR), Dịch vụ Google Play sẽ tự động lấy thông tin xác thực mạng Thread của Google để sử dụng.

Tích hợp SDK vào ứng dụng Android

Để bắt đầu, hãy hoàn tất các bước sau:

  1. Làm theo hướng dẫn trong phần Thiết lập Dịch vụ Google Play.

  2. Thêm phần phụ thuộc Dịch vụ Google Play vào tệp build.gradle:

    implementation 'com.google.android.gms:play-services-threadnetwork:16.2.1'
    
  3. Không bắt buộc: Xác định một lớp dữ liệu BorderAgent để lưu trữ thông tin TBR. Chúng tôi sẽ sử dụng dữ liệu này trong suốt hướng dẫn này:

    data class BorderAgentInfo(
      // Network Name max 16 len
      val networkName: String = "",
      val extPanId: ByteArray = ByteArray(16),
      val borderAgentId: ByteArray = ByteArray(16),
      ...
    )
    

Tiếp theo, chúng ta sẽ tìm hiểu các bước nên thực hiện để thêm và quản lý thông tin xác thực ưu tiên.

Thiết lập mới cho Bộ định tuyến biên Thread

Trước khi tạo mạng mới cho bộ định tuyến biên mới, bạn nên thử sử dụng thông tin xác thực mạng ưu tiên trước. Điều này đảm bảo rằng các thiết bị Thread được kết nối với một mạng Thread duy nhất khi có thể.

Lệnh gọi đến getPreferredCredentials sẽ khởi chạy một Hoạt động, nhắc người dùng cho phép yêu cầu mạng. Nếu thông tin xác thực mạng đã được lưu trữ trong chuỗi khoá kỹ thuật số của Thread SDK, thì thông tin xác thực sẽ được trả về ứng dụng của bạn.

Yêu cầu thông tin đăng nhập

Cách nhắc người dùng nhập thông tin xác thực ưu tiên:

  1. Khai báo một ActivityLauncher:

    private lateinit var preferredCredentialsLauncher: ActivityResultLauncher<IntentSenderRequest>
    
  2. Xử lý kết quả Hoạt động, được trả về dưới dạng 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. Nếu đang thiết lập một TBR mới, bạn nên gọi preferredCredentials và chạy Hoạt động. Lệnh gọi này sẽ đảm bảo rằng TBR mới của bạn sẽ sử dụng cùng thông tin xác thực đã được lưu trữ dưới dạng ưu tiên trong điện thoại, thúc đẩy sự hội tụ của các TBR khác nhau vào cùng một mạng.

    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. Nếu trường hợp sử dụng của bạn liên quan đến việc thiết lập các thiết bị không phải TBR, chẳng hạn như thiết bị cuối Matter-over-Thread mới, bạn nên sử dụng API allActiveCredentials để tìm nạp thông tin xác thực. Lệnh gọi này sẽ quét tìm TBR trong mạng cục bộ, do đó sẽ không trả về thông tin xác thực không có sẵn trong TBR cục bộ hiện có.

    // 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
    }
    

Tạo mạng Thread mới

Nếu không có thông tin đăng nhập mạng Thread ưu tiên cũng như thông tin đăng nhập Thread đang hoạt động trong mạng Thread của người dùng, thì bạn có thể sử dụng API addCredentials để thêm thông tin đăng nhập vào Dịch vụ Google Play. Để làm việc này, bạn cần tạo một ThreadBorderAgent, đồng thời cung cấp một đối tượng ThreadNetworkCredentials.

Để tạo một mạng ngẫu nhiên, hãy gọi newRandomizeBuilder:

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

Cách chỉ định Tên mạng Thread:

val threadCredentials = ThreadNetworkCredentials.newRandomizedBuilder()
  .setNetworkName("ThreadNetworkSDK")
  .build()

Thêm thông tin đăng nhập

Để cung cấp thông tin đăng nhập mạng Thread cho các nhà cung cấp Thread khác, chúng tôi cần thêm thông tin đăng nhập đó vào Dịch vụ Google Play. Trước khi có thể thêm thông tin xác thực mới, chúng ta cũng cần biết mạng Thread này thuộc về thiết bị TBR nào.

Trong ví dụ này, chúng ta sẽ tạo một ThreadBorderAgent từ Mã nhận dạng tác nhân biên và truyền thông tin đăng nhập mạng Thread mới mà bạn vừa tạo:

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

Phát hiện và di chuyển border router trong trường

Nếu có border router tại hiện trường, bạn có thể sử dụng isPreferredCredentials để xác định xem border router của bạn có thuộc mạng ưu tiên hay không. API này không nhắc người dùng cấp quyền và kiểm tra thông tin xác thực border router so với thông tin được lưu trữ trong Dịch vụ Google Play.

isPreferredCredentials trả về 0 nếu không khớp và 1 nếu khớp, dưới dạng kiểu dữ liệu Int. Bạn có thể sử dụng IsPreferredCredentialsResult để kiểm tra kết quả.

public @interface IsPreferredCredentialsResult {
    int PREFERRED_CREDENTIALS_NOT_FOUND = -1;
    int PREFERRED_CREDENTIALS_NOT_MATCHED = 0;
    int PREFERRED_CREDENTIALS_MATCHED = 1;
}

Để sử dụng isPreferredCredentials, trước tiên, bạn cần tạo một đối tượng ThreadNetworkCredentials. Có một số cách để tạo thực thể ThreadNetworkCredentials. Trong các bước tiếp theo, chúng ta sẽ tìm hiểu các lựa chọn này.

Thông tin đăng nhập mạng Thread theo Tập dữ liệu hoạt động

Có những trường hợp TBR của bạn đã được thiết lập bằng một mạng Thread và bạn muốn thêm mạng Thread này vào Dịch vụ Google Play để chia sẻ với các nhà cung cấp khác. Bạn có thể tạo một thực thể ThreadNetworkCredential từ danh sách TLV của Tập dữ liệu hoạt động đang hoạt động của Thread thô:

  1. Chuyển đổi Tập dữ liệu hoạt động thành ByteArray. Ví dụ:

    val activeDataset =
          "0e080000000000010000000300000f35060004001fffe0020833333333...".dsToByteArray()
    
    fun String.dsToByteArray(): ByteArray {
      return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
    }
    
  2. Sử dụng fromActiveOperationalDataset để tạo ThreadNetworkCredentials. Khi thành công, bạn sẽ có thể lấy Tên mạng Thread, Kênh và thông tin mạng khác. Để xem danh sách đầy đủ các thuộc tính, hãy tham khảo ThreadNetworkCredentials.

    val threadNetworkCredentials =
        ThreadNetworkCredentials.fromActiveOperationalDataset(activeDataset)
    Log.d(
        "threadNetworkCredentials",
        threadNetworkCredentials.channel.toString() + " - " + threadNetworkCredentials.networkName)
    
  3. Gọi API isPreferredCredentials và truyền 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}]") }
    

Thông tin đăng nhập mạng Thread theo Border Agent

Mã nhận dạng Border Agent xác định riêng biệt một thiết bị TBR. Để sử dụng API getCredentialsByBorderAgent, trước tiên, bạn cần tạo một đối tượng ThreadBorderAgent và truyền mã Border Agent ID.

Sau khi bạn tạo đối tượng ThreadBorderAgent, hãy gọi getCredentialsByBorderAgent. Nếu thông tin đăng nhập đã được lưu, hãy kiểm tra xem thông tin đó có được ưu tiên hay không.

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

Thông tin đăng nhập mạng Thread theo mã PAN mở rộng

Tương tự như getPreferredCredentials, bạn cũng có thể nhắc người dùng nhập thông tin đăng nhập từ Mã PAN mở rộng của TBR. getCredentialsByExtendedPanId trả về một IntentSender và kết quả Hoạt động chứa một đối tượng ThreadNetworkCredentials khi người dùng phê duyệt.

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

Xoá thông tin đăng nhập

Khi thiết bị border router của bạn bị xoá khỏi nhà hoặc đặt lại về trạng thái ban đầu, bạn cần xoá mạng Thread của thiết bị đó khỏi Dịch vụ Google Play.

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

Tài nguyên

Để tìm hiểu thêm về Thread Network SDK, hãy tham khảo Tài liệu tham khảo API.