SDK сети потоков для 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 Setup: это ваше приложение для Android, которое настраивает новые устройства Thread Border Router и добавляет учетные данные сети Thread в сервисы Google Play. Ваше приложение является полномочным владельцем добавленных учетных данных и имеет к ним доступ.

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

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

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

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

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

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

Пограничные маршрутизаторы потока должны использовать mDNS для объявления информации о сети потока, включая сетевое имя, расширенный идентификатор панорамы и идентификатор пограничного агента. Соответствующие значения 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 запускает Activity, предлагая пользователям разрешить сетевой запрос. Если сетевые учетные данные были сохранены в цифровой цепочке для ключей Thread SDK, учетные данные возвращаются в ваше приложение.

Запрос учетных данных

Чтобы запросить у пользователя предпочтительные учетные данные:

  1. Объявите ActivityLauncher :

    private lateinit var preferredCredentialsLauncher: ActivityResultLauncher<IntentSenderRequest>
    
  2. Обработайте результат 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.")
       }
     }
    
  3. Вызовите preferredCredentials и запустите Activity:

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

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

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

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

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

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

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

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

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

Сетевые учетные данные потока от пограничного агента

Идентификатор пограничного агента однозначно идентифицирует устройство пограничного маршрутизатора. Чтобы использовать 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}]") }
}

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

Подобно getPreferredCredentials , вы также можете запросить у пользователя учетные данные из расширенного идентификатора панорамы граничного маршрутизатора. 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 удалено из вашего дома или сброшено до заводских настроек, вам необходимо удалить его сеть 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 .