SDK Thread Network для Android

SDK Thread Network предоставляет функциональность, аналогичную цифровой связке ключей, позволяя вашим приложениям Android обмениваться учетными данными сети Thread со службами Google Play. Это позволяет вашим приложениям настраивать любое устройство Thread из любой экосистемы умного дома, не раскрывая учетные данные и пользовательские данные напрямую.

С помощью всего лишь нескольких вызовов API вы можете:

  1. Запросите предпочтительные учетные данные сети Thread в сервисах Google Play.
  2. Настройте новые пограничные маршрутизаторы и добавьте учетные данные сети Thread в сервисы Google Play.
  3. Если у вас уже есть пограничные маршрутизаторы в полевых условиях, вы можете проверить, находятся ли ваши пограничные маршрутизаторы в предпочтительной сети, и при необходимости перенести их.

Необходимо рассмотреть несколько путей развития пользователей и разработчиков. В этом руководстве мы рассмотрим большинство из них, а также другие ключевые функции и рекомендации по использованию.

Основная терминология и концепции API

Прежде чем приступить к работе, полезно понять следующие термины:

  • Учетные данные сети потоков: двоичный объект TLV потоков, который кодирует имя сети потоков, ключ сети и другие свойства, необходимые устройству потоков для присоединения к данной сети потоков.

  • Предпочитаемые сетевые учетные данные потока: автоматически выбираемые сетевые учетные данные потока, которые можно использовать совместно с приложениями разных поставщиков с помощью API getPreferredCredentials .

  • Идентификатор пограничного агента: 16-байтовый глобальный уникальный идентификатор устройства Thread Border Router. Этот идентификатор создается и управляется поставщиками пограничных маршрутизаторов.

  • Приложение настройки Thread Border Router: это ваше приложение для Android, которое настраивает новые устройства Thread Border Router и добавляет сетевые учетные данные Thread в сервисы Google Play. Ваше приложение является полноправным владельцем добавленных учетных данных и имеет к ним доступ.

Многие API-интерфейсы Thread Network возвращают задачу , которая выполняется асинхронно. Вы можете использовать addOnSuccessListener и addOnFailureListener для регистрации обратных вызовов для получения результата. Чтобы узнать больше, обратитесь к документации задачи .

Владение и обслуживание учетных данных

Приложение, которое добавляет сетевые учетные данные Thread, становится владельцем учетных данных и имеет полные разрешения на доступ к учетным данным. Если вы попытаетесь получить доступ к учетным данным, добавленным другими приложениями, вы получите ошибку PERMISSION_DENIED .

Как владельцу приложения рекомендуется обновлять учетные данные, хранящиеся в службах Google Play, при обновлении сети Thread Border Router. Это означает добавление учетных данных при необходимости, обновление учетных данных при изменении сетевых учетных данных Thread пограничного маршрутизатора и удаление учетных данных при удалении пограничного маршрутизатора Thread или сбросе настроек до заводских.

Обнаружение пограничного агента

Учетные данные должны быть сохранены с идентификатором пограничного агента. Вам необходимо убедиться, что ваше приложение настройки пограничного маршрутизатора Thread может определить идентификаторы пограничного агента ваших пограничных маршрутизаторов Thread.

Граничные маршрутизаторы потоков должны использовать mDNS для объявления информации о сети потоков, включая имя сети, расширенный идентификатор Pan и идентификатор пограничного агента. Соответствующие значения txt для этих атрибутов — nn , xp и id соответственно.

В сетях с пограничными маршрутизаторами 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. Обработайте результат действия, возвращенный как 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, вы можете использовать API addCredentials для добавления учетных данных в Сервисы Google Play. Для этого вам потребуется создать ThreadBorderAgent , а также предоставить объект ThreadNetworkCredentials .

Чтобы создать случайную сеть, вызовите newRandomizeBuilder :

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

Чтобы указать имя сети потока:

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

Добавить учетные данные

Чтобы сделать ваши сетевые учетные данные Thread доступными для других поставщиков Thread, нам необходимо добавить их в службы Google Play. Прежде чем мы сможем добавить наши новые учетные данные, нам также необходимо знать, какому устройству пограничного маршрутизатора принадлежит эта сеть потоков.

В этом примере мы создадим 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, чтобы поделиться ею с другими поставщиками. Вы можете создать экземпляр ThreadNetworkCredential из необработанного списка TLV набора активных рабочих данных потока:

  1. Преобразуйте набор операционных данных в ByteArray . Например:

    val activeDataset =
          "0e080000000000010000000300000f35060004001fffe0020833333333...".dsToByteArray()
    
    fun String.dsToByteArray(): ByteArray {
      return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
    }
    
  2. Используйте fromActiveOperationalDataset для создания ThreadNetworkCredentials . В случае успеха вы сможете получить имя сети потока, канал и другую сетевую информацию. Полный список свойств см. в ThreadNetworkCredentials .

    val threadNetworkCredentials =
        ThreadNetworkCredentials.fromActiveOperationalDataset(activeDataset)
    Log.d(
        "threadNetworkCredentials",
        threadNetworkCredentials.channel.toString() + " - " + threadNetworkCredentials.networkName)
    
  3. Вызовите API isPreferredCredentials и передайте 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}]") }
    

Сетевые учетные данные потока от Border Agent

Идентификатор пограничного агента однозначно идентифицирует устройство пограничного маршрутизатора. Чтобы использовать API getCredentialsByBorderAgent , сначала вам необходимо создать объект ThreadBorderAgent и передать идентификатор пограничного агента.

После создания объекта 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}]") }
}

Сетевые учетные данные потока по расширенному идентификатору Pan

Подобно getPreferredCredentials , вы также можете запросить у пользователя учетные данные из расширенного идентификатора Pan пограничного маршрутизатора. getCredentialsByExtendedPanId возвращает IntentSender , а результат действия содержит объект 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 удалено из вашего дома или выполнено восстановление заводских настроек, вам необходимо удалить его сеть Thread из сервисов 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}]") }
}

Ресурсы

Дополнительные сведения о пакете SDK Thread Network см. в справочнике по API .