The Thread Network SDK provides functionality that's similar to a digital
keychain, allowing your Android apps to share Thread network credentials with
Google Play services. This allows your apps to set up any Thread device from
any smart home ecosystem, without exposing credentials and user data directly.

With just a few API calls, you can:

1. Request preferred Thread network credentials from Google Play services.
2. Set up new Thread Border Router (TBR)s and add your Thread network credentials to Google Play services.
3. If you already have in-field TBRs, you can check if your TBRs are in the preferred network and migrate them, if necessary.

There are several user and developer journeys to consider. We'll cover most of
them in this guide, along with other key features and recommended usage.

> [!NOTE]
> **Note:** Starting from version `16.0.0`, the Thread Network SDK supports Android 8.0+ (API level 26+). But for Android 8.0, mobile phones won't be able to connect to Thread end devices because [Route Information Option](https://www.rfc-editor.org/rfc/rfc4191#section-2.3) (RIO) is only supported on Android 8.1+.

## Key terminology and API concepts

Before you get started, it's helpful to understand the following terms:

- **Thread Network Credentials:** Binary blob of Thread TLVs that encodes
  Thread Network Name, Network Key and other properties that are required by
  a Thread device to join a given Thread network.

- **Preferred Thread Network Credentials:** The auto-selected Thread network
  credentials that can be shared with apps of different vendors using the
  `getPreferredCredentials` API.

- **Border Agent ID:** A 16-byte globally unique ID for a
  TBR
  device. This ID is created and managed by border router vendors.

- **TBR setup app:** This is your Android app that sets
  up new TBR devices and adds the Thread network
  credentials to Google Play services. Your app is the authoritative owner of
  the added credentials and has access to them.

Many of the Thread Network APIs return a
[Task](https://developers.home.google.com/android/reference/com/google/android/gms/tasks/Task)
that completes asynchronously. You can use
[addOnSuccessListener](https://developers.home.google.com/android/reference/com/google/android/gms/tasks/Task#public-abstract-tasktresult-addonsuccesslistener-onsuccesslistener-super-tresult-listener)
and [addOnFailureListener](https://developers.home.google.com/android/reference/com/google/android/gms/tasks/Task#public-abstract-tasktresult-addonfailurelistener-onfailurelistener-listener)
to register callbacks for receiving the result. To learn more, refer to the
[Task](https://developers.home.google.com/android/reference/com/google/android/gms/tasks/Task)
documentation.

## Credentials ownership and maintenance

The app that adds the Thread network credentials becomes the owner of the
credentials, and has full permissions to access to the credentials. If you try
to access credentials added by other apps, you'll receive a `PERMISSION_DENIED`
error.

As the app owner, it's recommended that you keep credentials stored in Google
Play services up-to-date when the TBR network is updated.
This means adding credentials when required, updating credentials when the
border router's Thread network credentials change, and removing
credentials when the TBR is removed or factory reset.

## Border Agent discovery

Credentials must be saved with a Border Agent ID. You'll need to make sure that
your TBR setup app is able to determine the Border Agent
IDs of your TBRs.

TBRs must use mDNS to advertise Thread network information,
including the Network Name, Extended Pan ID, and Border Agent ID. The
corresponding `txt` values for these attributes are `nn`, `xp`, and `id`,
respectively.

> [!NOTE]
> **Note:** It's important for the border router device to include a 16-byte `id` TXT value in its MeshCoP mDNS service for Google Play services to discover and identify the Thread network. For products using the open source [ot-br-posix](https://github.com/openthread/ot-br-posix), make sure that the `OTBR_PUBLISH_MESHCOP_BA_ID` feature is enabled.

For networks with Google Thread Border Router (gTBR)s, Google Play services automatically
gets Google Thread network credentials for use.

## Integrate the SDK into your Android app

To get started, complete the following steps:

1. Follow the instructions provided at
   [Set up Google Play services](https://developers.home.google.com/android/guides/setup).

2. Add the Google Play services dependency to your `build.gradle` file:

       implementation 'com.google.android.gms:play-services-threadnetwork:16.2.1'

3. *Optional:* Define a `BorderAgent` data class to store
   TBR information. We'll use this data throughout
   this guide:

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

Next, we'll go over the recommended steps to add and manage preferred
credentials.

## New Thread Border Router setups

Before you create a new network for new border routers, it's important
that you try using the preferred network credentials first. This ensures that
Thread devices are connected to a single Thread network when possible.

A call to `getPreferredCredentials` launches
an Activity, prompting users to allow the network request. If network
credentials have been stored in the Thread SDK digital keychain, the credentials
are returned to your app.

### Request credentials

To prompt the user for preferred credentials:

1. Declare an `ActivityLauncher`:

       private lateinit var preferredCredentialsLauncher: ActivityResultLauncher<IntentSenderRequest>

2. Handle the Activity result, returned as `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. If you're setting up a new TBR, it's advised that you
   call `preferredCredentials` and launch the Activity. This call will ensure
   that your new TBR will use the same credentials already
   stored as *preferred* in the phone, promoting the convergence of different
   TBRs to the same network.

       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. If your use case relates to setting up non-TBR devices, such as a new
   Matter-over-Thread end device, it's advised that you use the `allActiveCredentials`
   api to fetch credentials. This call will scan for TBRs found in the local
   network and thus won't return credentials that are not available by an
   existing TBR locally.

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

### Create a new Thread network

If there are neither preferred Thread network credentials nor active thread
credentials available in a user's
Thread network, then you can use the `addCredentials` API to add credentials to
Google Play services. To do this, you'll need to create a `ThreadBorderAgent`,
and also supply a `ThreadNetworkCredentials` object.

To create a random network, call the `newRandomizeBuilder`:

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

To specify the Thread network Name:

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

### Add credentials

To make your Thread network credentials available for other Thread vendors,
we need to add them to Google Play services. Before we can add our new
credentials, we also need to know to which TBR device this
Thread network belongs to.

In this example, we'll create a `ThreadBorderAgent` from a Border Agent ID, and
pass the new Thread network credentials that you just created:

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

> [!NOTE]
> **Note:** The Border Agent ID **MUST** be the `id` TXT value of the border router mDNS MeshCoP service.

> [!NOTE]
> **Note:** You should use `addCredentials` to update the preferred credentials when a border router's Thread network credentials change. Existing Thread network credentials that are associated with the same border router will be overwritten.

## Detect and migrate in-field border routers

If you have in-field border routers, you can use
`isPreferredCredentials` to determine if your border routers belong
to the preferred network. This API does not prompt the
user for permission, and checks border router credentials against
what's stored in Google Play services.

`isPreferredCredentials` returns `0` for not matched, and `1` for
matched, as an `Int` data type. You can use `IsPreferredCredentialsResult`
to check your results.

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

To use `isPreferredCredentials`, you'll need to create a
`ThreadNetworkCredentials` object first. There are several ways to instantiate `ThreadNetworkCredentials`. In the next steps, we'll go over these options.

### Thread network credentials by Operational Dataset

There are cases that your TBR is already set up with a
Thread network, and you want to add this Thread network to Google Play services
to share it with other vendors. You can create a `ThreadNetworkCredential`
instance from a raw Thread Active Operational Dataset TLV list:

1. Convert the Operational Dataset to a `ByteArray`. For example:

       val activeDataset =
             "0e080000000000010000000300000f35060004001fffe0020833333333...".dsToByteArray()

       fun String.dsToByteArray(): ByteArray {
         return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
       }

2. Use `fromActiveOperationalDataset` to create the `ThreadNetworkCredentials`.
   When successful, you'll be able to get the Thread network Name, Channel, and
   other network information. For a complete list of properties, refer to
   [ThreadNetworkCredentials](https://developers.home.google.com/reference/com/google/android/gms/threadnetwork/ThreadNetworkCredentials).

       val threadNetworkCredentials =
           ThreadNetworkCredentials.fromActiveOperationalDataset(activeDataset)
       Log.d(
           "threadNetworkCredentials",
           threadNetworkCredentials.channel.toString() + " - " + threadNetworkCredentials.networkName)

3. Call the `isPreferredCredentials` API and pass the
   `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}]") }

### Thread network credentials by Border Agent

A Border Agent ID uniquely identifies a TBR device. To use
the `getCredentialsByBorderAgent` API, first you'll need to create a
`ThreadBorderAgent` object and pass the Border Agent ID.

Once you've created the `ThreadBorderAgent` object, call
`getCredentialsByBorderAgent`. If the credentials have been saved, check to see
if they're preferred.

> [!IMPORTANT]
> **Important:** This API will fail if the credentials were not added by your app.

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

### Thread network credentials by Extended Pan ID

Similar to `getPreferredCredentials`, you can also prompt the user for
credentials from a TBR's Extended Pan ID. The
`getCredentialsByExtendedPanId` returns an `IntentSender`, and the Activity
result contains a `ThreadNetworkCredentials` object when the user approves.

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

## Remove Credentials

When your border router device is removed from your home or factory
reset, you need to remove its Thread network from 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}]") }
    }

## Resources

To learn more about the Thread Network SDK, refer to the
[API Reference](https://developers.home.google.com/reference/com/google/android/gms/threadnetwork/package-summary).