SDK de rede do Thread para Android

O SDK da rede Thread oferece funcionalidades semelhantes a um chaveiro digital, permitindo que seus apps Android compartilhem credenciais da rede Thread com o Google Play Services. Isso permite que seus apps configurem qualquer dispositivo Thread de qualquer ecossistema de casa inteligente, sem expor credenciais e dados do usuário diretamente.

Com apenas algumas chamadas de API, você pode:

  1. Solicitar credenciais de rede Thread preferenciais do Google Play Services.
  2. Configurar novos Thread Border Router (TBR)s e adicionar as credenciais da rede Thread ao Google Play Services.
  3. Se você já tiver TBRs em campo, poderá verificar se seus TBRs estão na rede preferencial e migrá-los, se necessário.

Há várias jornadas de usuários e desenvolvedores a serem consideradas. Vamos abordar a maioria delas neste guia, além de outros recursos importantes e uso recomendado.

Terminologia e conceitos principais da API

Antes de começar, é útil entender os seguintes termos:

  • Credenciais da rede Thread:blob binário de TLVs do Thread que codifica o nome da rede Thread, a chave de rede e outras propriedades necessárias para que um dispositivo Thread entre em uma determinada rede Thread.

  • Credenciais de rede Thread preferenciais:as credenciais de rede Thread selecionadas automaticamente que podem ser compartilhadas com apps de diferentes fornecedores usando a API getPreferredCredentials.

  • ID do agente de borda: um ID globalmente exclusivo de 16 bytes para um TBR dispositivo. Esse ID é criado e gerenciado por border router fornecedores.

  • App de configuração do TBR:é o app Android que configura novos dispositivos TBR e adiciona as credenciais da rede Thread ao Google Play Services. Seu app é o proprietário autorizado das credenciais adicionadas e tem acesso a elas.

Muitas das APIs da rede Thread retornam uma tarefa que é concluída de forma assíncrona. Você pode usar addOnSuccessListener e addOnFailureListener para registrar callbacks para receber o resultado. Para saber mais, consulte a documentação da tarefa.

Propriedade e manutenção de credenciais

O app que adiciona as credenciais da rede Thread se torna o proprietário delas e tem permissões completas para acessá-las. Se você tentar acessar credenciais adicionadas por outros apps, vai receber um erro PERMISSION_DENIED.

Como proprietário do app, é recomendável manter as credenciais armazenadas no Google Play Services atualizadas quando a rede TBR for atualizada. Isso significa adicionar credenciais quando necessário, atualizar as credenciais quando as credenciais da rede Thread do border router mudarem e remover credenciais quando o TBR for removido ou redefinido para a configuração de fábrica.

Descoberta de agentes de borda

As credenciais precisam ser salvas com um ID de agente de borda. É necessário garantir que seu TBR app de configuração possa determinar os IDs de agente de borda de seus TBRs.

TBRs precisam usar o mDNS para anunciar informações da rede Thread, incluindo o nome da rede, o ID Pan estendido e o ID do agente de borda. Os valores txt correspondentes para esses atributos são nn, xp e id, respectivamente.

Para redes com Google Thread Border Router (gTBR)s, o Google Play Services recebe automaticamente as credenciais da rede do Google Thread para uso.

Integrar o SDK ao seu app Android

Para começar, siga estas etapas:

  1. Siga as instruções fornecidas em Configurar o Google Play Services.

  2. Adicione a dependência do Google Play Services ao arquivo build.gradle:

    implementation 'com.google.android.gms:play-services-threadnetwork:16.2.1'
    
  3. Opcional: defina uma classe de dados BorderAgent para armazenar TBR informações. Vamos usar esses dados neste guia:

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

Em seguida, vamos analisar as etapas recomendadas para adicionar e gerenciar credenciais preferenciais.

Novas configurações de roteador de borda do Thread

Antes de criar uma nova rede para novos roteadores de borda, é importante tentar usar as credenciais de rede preferenciais primeiro. Isso garante que os dispositivos Thread estejam conectados a uma única rede Thread quando possível.

Uma chamada para getPreferredCredentials inicia uma atividade, solicitando que os usuários permitam a solicitação de rede. Se as credenciais de rede tiverem sido armazenadas no chaveiro digital do SDK do Thread, elas serão retornadas ao seu app.

Solicitar credenciais

Para solicitar as credenciais preferenciais ao usuário:

  1. Declare um ActivityLauncher:

    private lateinit var preferredCredentialsLauncher: ActivityResultLauncher<IntentSenderRequest>
    
  2. Processe o resultado da atividade, retornado como 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. Se você estiver configurando um novo TBR, é recomendável que você chame preferredCredentials e inicie a atividade. Essa chamada garante que o novo TBR use as mesmas credenciais já armazenadas como preferenciais no smartphone, promovendo a convergência de diferentes TBRs para a mesma rede.

    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. Se o caso de uso estiver relacionado à configuração de dispositivos que não são TBRs, como um novo dispositivo final Matter-over-Thread, é recomendável usar a API allActiveCredentials para buscar credenciais. Essa chamada vai procurar TBRs encontrados na rede local e, portanto, não vai retornar credenciais que não estão disponíveis por um TBR local.

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

Criar uma nova rede Thread

Se não houver credenciais de rede Thread preferenciais nem credenciais de thread ativas disponíveis na rede Thread de um usuário, você poderá usar a API addCredentials para adicionar credenciais ao Google Play Services. Para fazer isso, você precisará criar um ThreadBorderAgent e também fornecer um objeto ThreadNetworkCredentials.

Para criar uma rede aleatória, chame o newRandomizeBuilder:

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

Para especificar o nome da rede Thread:

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

Adicionar credenciais

Para disponibilizar as credenciais da rede Thread para outros fornecedores do Thread, precisamos adicioná-las ao Google Play Services. Antes de adicionar as novas credenciais, também precisamos saber a qual dispositivo TBR essa rede Thread pertence.

Neste exemplo, vamos criar um ThreadBorderAgent de um ID de agente de borda e transmitir as novas credenciais de rede Thread que você acabou de criar:

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

Detectar e migrar border routers em campo

Se você tiver border routers em campo, poderá usar isPreferredCredentials para determinar se os border routers pertencem à rede preferencial. Essa API não solicita permissão ao usuário e verifica as border router credenciais em relação ao que está armazenado no Google Play Services.

isPreferredCredentials retorna 0 para não correspondente e 1 para correspondente, como um tipo de dados Int. Você pode usar IsPreferredCredentialsResult para verificar os resultados.

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

Para usar isPreferredCredentials, primeiro você precisa criar um objeto ThreadNetworkCredentials. Há várias maneiras de instanciar ThreadNetworkCredentials. Nas próximas etapas, vamos analisar essas opções.

Credenciais de rede Thread por conjunto de dados operacional

Há casos em que o TBR já está configurado com uma rede Thread, e você quer adicionar essa rede Thread ao Google Play Services para compartilhá-la com outros fornecedores. Você pode criar uma instância ThreadNetworkCredential de uma lista de TLVs de conjunto de dados operacional ativo do Thread bruto:

  1. Converta o conjunto de dados operacional em um ByteArray. Exemplo:

    val activeDataset =
          "0e080000000000010000000300000f35060004001fffe0020833333333...".dsToByteArray()
    
    fun String.dsToByteArray(): ByteArray {
      return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
    }
    
  2. Use fromActiveOperationalDataset para criar o ThreadNetworkCredentials. Quando bem-sucedido, você poderá acessar o nome da rede Thread, o canal e outras informações da rede. Para uma lista completa de propriedades, consulte ThreadNetworkCredentials.

    val threadNetworkCredentials =
        ThreadNetworkCredentials.fromActiveOperationalDataset(activeDataset)
    Log.d(
        "threadNetworkCredentials",
        threadNetworkCredentials.channel.toString() + " - " + threadNetworkCredentials.networkName)
    
  3. Chame a API isPreferredCredentials e transmita o 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}]") }
    

Credenciais de rede Thread por agente de borda

Um ID de agente de borda identifica exclusivamente um dispositivo TBR. Para usar a API getCredentialsByBorderAgent, primeiro você precisa criar um objeto ThreadBorderAgent e transmitir o ID do agente de borda.

Depois de criar o objeto ThreadBorderAgent, chame getCredentialsByBorderAgent. Se as credenciais tiverem sido salvas, verifique se elas são preferenciais.

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

Credenciais de rede Thread por ID Pan estendido

Semelhante a getPreferredCredentials, você também pode solicitar as credenciais do usuário de um TBR's ID Pan estendido. O getCredentialsByExtendedPanId retorna um IntentSender, e o resultado da atividade contém um objeto ThreadNetworkCredentials quando o usuário aprova.

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

Remover credenciais

Quando o dispositivo border router é removido da sua casa ou redefinido para a configuração de fábrica, é necessário remover a rede Thread do Google Play Services.

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

Recursos

Para saber mais sobre o SDK da rede Thread, consulte a referência da API.