SDK mạng Thread dành cho Android

Thread Network SDK cung cấp chức năng tương tự như một chuỗi khoá kỹ thuật số, cho phép các ứng dụng Android của bạn 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 các ứ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 tiết lộ trực tiếp thông tin đăng nhập 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 hiện trường, bạn có thể kiểm tra xem TBR của mình có nằm trong mạng lưới ưu tiên hay không và di chuyển chúng nếu cần.

Có một số hành trình của người dùng và nhà phát triển mà bạn cần cân nhắc. Chúng ta sẽ đề cập đến hầu hết các tính năng này 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.

Các thuật ngữ và khái niệm chính 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 các 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 chuỗi ưu tiên: Thông tin đăng nhập mạng chuỗi được chọn tự động có thể 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 tác nhân biên: 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ã nhận dạng này do các nhà cung cấp border router tạo và quản lý.

  • TBR ứng dụng thiết lập: Đây là ứng dụng Android giúp bạn 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 của 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 Mạng luồng trả về một Task hoàn tất không đồng bộ. Bạn có thể dùng addOnSuccessListeneraddOnFailureListener để đăng ký các 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 xác thực

Ứ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ó đầy đủ 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 đăng nhập 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 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à bạn phải 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 đăng nhập bằng 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 các TBR.

TBR phải sử dụng mDNS để thông báo thông tin mạng Thread, bao gồm cả Tên mạng, Extended Pan ID và Border Agent ID. Các giá trị txt tương ứng cho các thuộc tính này lần lượt là nn, xpid.

Đố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 đăng nhập 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 được cung cấp tại 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ẽ xem xét các bước được đề xuất để thêm và quản lý thông tin đăng nhập ưu tiên.

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

Trước khi tạo một mạng mới cho các bộ định tuyến biên mới, bạn nên thử sử dụng thông tin đăng nhập 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ể.

Một 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ề cho ứ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 cung cấp thông tin đăng nhập ưu tiên:

  1. Khai báo một ActivityLauncher:

    private lateinit var preferredCredentialsLauncher: ActivityResultLauncher<IntentSenderRequest>
    
  2. Xử lý kết quả của 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à khởi chạy Hoạt động. Cuộc 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 đăng nhập đã đượ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ư một thiết bị cuối Matter-over-Thread mới, thì bạn nên dùng allActiveCredentialsapi để tìm nạp thông tin đăng nhập. Lệnh gọi này sẽ quét các TBR có trong mạng cục bộ và do đó sẽ không trả về những thông tin đăng nhập không có sẵn theo TBR hiện có tại địa phương.

    // 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ể 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 và 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 đó vào Dịch vụ Google Play. Trước khi có thể thêm thông tin đăng nhập 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 tại chỗ

Nếu có border router trong trường, bạn có thể dùng isPreferredCredentials để xác định xem border router có thuộc về 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 đăng nhập border router dựa trên thông tin được lưu trữ trong Dịch vụ Google Play.

isPreferredCredentials trả về 0 cho trường hợp không khớp và 1 cho trường hợp 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 về những 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 với 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 thô của luồng:

  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ể nhận được Tên mạng, Kênh và thông tin khác về mạng Thread. Để 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 của 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ã nhận dạng đại lý biên giới.

Sau khi 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ã nhận dạng mạng cá nhân mở rộng

Tương tự như getPreferredCredentials, bạn cũng có thể nhắc người dùng cung cấ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 xoá thiết bị border router 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 về API.