Berbagi Jaringan Thread dengan Google Thread Credentials API

1. Sebelum memulai

Dalam codelab Thread Border Router (TBR), kami menunjukkan cara membuat Router Pembatas Thread berdasarkan Raspberry Pi. Dalam codelab tersebut, kita

  • Membuat konektivitas IP Dua Arah antara jaringan Thread dan Wi-Fi/Ethernet.
  • Menyediakan penemuan layanan dua arah melalui mDNS (di link Wi-Fi/Ethernet) dan SRP (di jaringan Thread).

Codelab ini dibuat berdasarkan versi sebelumnya, yang membahas cara router pembatas dan aplikasi Anda dapat berinteraksi dengan Google API untuk membuat satu Jaringan Thread. Menggabungkan kredensial Thread penting karena menambah keandalan jaringan dan menyederhanakan interaksi pengguna dengan aplikasi yang mengandalkan Thread.

Prasyarat

  • Selesaikan Codelab OTBR
  • Pengetahuan dasar tentang jaringan Linux, Android/Kotlin, dan Thread

Yang akan Anda pelajari

  • Cara menggunakan Thread Sharing API untuk Mendapatkan dan Menetapkan set kredensial
  • Cara menyiapkan Router Pembatas OpenThread Anda sendiri dengan kredensial yang sama seperti jaringan Google

Yang Anda butuhkan

  • Board Raspberry Pi 4 atau board berbasis Linux lainnya yang menjalankan Open Thread Border Router (OTBR)
  • Board yang menyediakan konektivitas IEEE 802.15.4 sebagai Radio Co-Processor (RCP). Lihat daftar repositori dari berbagai vendor SoC dan petunjuknya di halaman GitHub OpenThread

2. Menyiapkan layanan HTTP

Elemen penyusun pertama yang kita butuhkan adalah antarmuka yang memungkinkan kita untuk membaca Active Credentials dan menulis Pending Credentials ke OTBR Anda. Saat membuat TBR, gunakan mekanisme milik Anda sendiri, seperti yang ditunjukkan di sini disertai dua contoh. Opsi pertama menunjukkan cara berinteraksi dengan agen OTBR secara lokal melalui DBUS, sedangkan opsi kedua memanfaatkan Rest API yang dapat dibangun di OTBR.

Tidak ada metode yang aman, dan tidak boleh digunakan apa adanya di lingkungan produksi. Namun, vendor dapat membuat enkripsi dengan salah satu metode tersebut untuk menggunakannya di lingkungan produksi, atau Anda dapat memperluas layanan monitor Anda sendiri untuk melakukan loopback HTTP atau panggilan DBUS lokal secara inheren.

Opsi 1: DBUS dan HTTP API di Python Script

91e5fdeed83e9354.pngS

Langkah ini membuat layanan HTTP dasar yang mengekspos dua endpoint untuk membaca dan menyetel kredensial, yang pada akhirnya memanggil perintah DBUS.

Pada RPi yang akan berfungsi sebagai OTBR Anda, instal dependensi Python 3:

$ pip install dbus-python shlex json

Jalankan skrip sebagai:

$  sudo python credentials_server.py 8081
serving at port 8081

Contoh ini menyiapkan server HTTP di port 8081 dan memproses di jalur root, baik untuk permintaan GET guna mengambil kredensial Thread, maupun permintaan POST untuk menetapkan kredensial Thread. Payload selalu berupa struktur JSON dengan TLV.

Permintaan PUT berikut menetapkan Pending Thread Credentials baru ke OTBR melalui jalur /node/dataset/pending. Dalam hal ini, kredensial yang tertunda akan diterapkan setelah 10 detik:

PUT /node/dataset/pending
Host: <IP>:8081
ContentType: "application/json"
acceptMimeType: "application/json"
...
{
        "ActiveDataset": "<TLV encoded new Thread Dataset>"
"PendingTimestamp": {
        "Seconds": <Unix timestamp in seconds>,
        "Ticks": 0,
        "Authoritative": false
},
"Delay": 10000 // in milliseconds
}

Permintaan GET ke /node/dataset/active mengambil Kredensial Aktif saat ini.

GET /node/dataset/active
Host: <IP>:8081
ContentType = "application/json"
acceptMimeType = "text/plain"
...
<TLV encoded Thread Dataset>

Skrip ini memanggil perintah DBUS R/W ke jalur bus io.openthread.BorderRouter.wpan0, jalur objek /io/openthread/BorderRouter/wpan0

# D-BUS interface
def call_dbus_method(interface, method_name, *arguments):
    bus = dbus.SystemBus()
    obj = bus.get_object('io.openthread.BorderRouter.wpan0', '/io/openthread/BorderRouter/wpan0')
    iface = dbus.Interface(obj, interface)
    method = getattr(iface, method_name)
    res = method(*arguments)
    return res

def get_dbus_property(property_name):
    return call_dbus_method('org.freedesktop.DBus.Properties', 'Get', 'io.openthread.BorderRouter',
                                 property_name)

def set_dbus_property(property_name, property_value):
    return call_dbus_method('org.freedesktop.DBus.Properties', 'Set', 'io.openthread.BorderRouter',
                                 property_name, property_value)                               

DBUS memungkinkan introspeksi kemampuannya. Anda dapat melakukannya sebagai:

$ sudo dbus-send --system --dest=io.openthread.BorderRouter.wpan0 \
        --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 \
        org.freedesktop.DBus.Introspectable.Introspect

Anda juga dapat memeriksa kemampuan yang didukung yang didokumentasikan di sini.

Opsi 2: HTTP Rest API native Agen OTBR

c748ca5151b6cacb.png

Router Pembatas OpenThread dibuat secara default dengan flag REST_API=1, yang mengaktifkan REST API. Jika build Anda dari codelab sebelumnya tidak mengaktifkan REST API, pastikan untuk membangun OTBR pada RPi Anda dengan flag tersebut:

$ REST_API=1 INFRA_IF_NAME=wlan0 ./script/setup

Agen OTBR dapat dimulai ulang dengan menjalankan:

$ sudo systemctl restart otbr-agent.service

Agen memulai server HTTP pada port 8081. Server ini memungkinkan pengguna atau program monitor melakukan banyak tugas di OTBR (didokumentasikan di sini). Anda dapat menggunakan browser, curl atau wget untuk memeriksa kontennya. Di antara banyak jalur yang didukung adalah kasus penggunaan yang dijelaskan di atas, dengan kata kerja GET pada /node/dataset/active dan kata kerja PUT di /node/dataset/pending

3. Menyiapkan Framework Kredensial di Android

Kredensial Pilihan

Layanan Google Play di Android mengizinkan dan mengharapkan pendaftaran kredensial untuk semua TBR di jaringan Anda. Masing-masing diidentifikasi berdasarkan Border Router Agent ID (BAID). Anda akan menggunakan metode addCredentials() dari antarmuka ThreadNetworkClient untuk melakukan tugas ini. TBR pertama yang ditambahkan ke penyimpanan Layanan Google Play menentukan Kredensial Pilihan untuk perangkat seluler ini.

Aplikasi yang menambahkan serangkaian kredensial jaringan Thread ke BAID-nya akan menjadi pemilik kredensial, dan memiliki izin penuh untuk mengaksesnya. Jika mencoba mengakses kredensial yang ditambahkan oleh aplikasi lain, Anda akan menerima error PERMISSION_DENIED. Namun, kredensial pilihan selalu tersedia untuk aplikasi apa pun setelah izin pengguna. Sebaiknya selalu perbarui kredensial yang disimpan di layanan Google Play saat jaringan Router Pembatas Thread diupdate. Meskipun informasi tersebut tidak digunakan saat ini, kami dapat memberikan perjalanan yang lebih baik pada masa mendatang.

Meskipun TBR pertama kemudian dikecualikan, Kredensial Pilihan akan tetap ada di perangkat Android. Setelah ditetapkan, Aplikasi lain yang mengelola kredensial Thread dapat memperoleh kredensial tersebut dari panggilan getPreferredCredentials().

Sinkronisasi Google TBR

Perangkat Android disinkronkan dengan TBR Google secara otomatis. Jika tidak ada kredensial di Android, perangkat akan mengekstraknya dari TBR Google di jaringan Anda, dan kredensial tersebut akan menjadi Kredensial Pilihan. Sinkronisasi antara TBR dan perangkat Android hanya terjadi jika TBR disambungkan dengan satu pengguna, atau jika disambungkan dengan dua pengguna yang berada di Smart Home yang sama ( Struktur).

Proses ini juga akan terjadi jika pengguna Google lain menggunakan GHA untuk Android atau GHA untuk iOS jika pengguna tersebut berada di Struktur yang sama. Dalam kasus GHA untuk iOS, kredensial pilihan ditetapkan di penyimpanan iOS, jika tidak ada kredensial pilihan.

Jika dua perangkat Android (atau Android + iGHA) berada di jaringan yang sama dengan kumpulan kredensial pilihan yang berbeda, perangkat yang awalnya mengonfigurasi TBR akan berlaku di TBR.

Orientasi TBR pihak ketiga

Penyimpanan kredensial saat ini tidak dicakup oleh Smart Home pengguna ( Struktur). Setiap perangkat Android akan memiliki penyimpanan BAID-nya. Namun, jika ada TBR Google di jaringan, perangkat Android dan perangkat iOS lain yang menjalankan Aplikasi Google Home untuk iOS akan disinkronkan dengan TBR tersebut dan mencoba menetapkan kredensial lokal di penyimpanan ponsel.

Sebelum OOB TBR baru membuat jaringan, penting untuk memeriksa apakah jaringan pilihan sudah ada di penyimpanan Android.

  • Jika jaringan pilihan sudah ada, vendor harus menggunakannya. Tindakan ini memastikan bahwa perangkat Thread terhubung ke satu jaringan Thread jika memungkinkan.
  • Jika tidak ada jaringan yang dipilih, buat kumpulan kredensial baru dan tetapkan ke TBR Anda di Layanan Google Play. Android akan menghormati kredensial tersebut sebagai kredensial standar yang ditetapkan di semua TBR yang berbasis di Google, dan vendor lainnya akan dapat meningkatkan jangkauan mesh Anda dan keandalan dengan perangkat tambahan

cd8bc726f67b1fa1.png

4. Meng-clone dan Memodifikasi Aplikasi Android

Kami telah membuat Aplikasi Android yang menampilkan kemungkinan panggilan utama ke Thread API. Anda dapat menggunakan pola-pola ini di aplikasi Anda. Dalam codelab ini, kita akan meng-clone Aplikasi Contoh Google Home untuk Matter dari GitHub.

Semua kode sumber yang ditampilkan di sini sudah dikodekan di aplikasi contoh. Anda diundang untuk memodifikasinya sesuai kebutuhan, tetapi Anda cukup meng-clone aplikasi atau menjalankan biner yang telah dibuat sebelumnya untuk memeriksa fungsinya.

  1. Clone menggunakan:
$ git clone https://github.com/google-home/sample-apps-for-matter-android.git
  1. Download dan buka Android Studio.
  2. Klik File > Buka dan arahkan ke repositori yang di-clone.
  3. Aktifkan mode developer di ponsel Android.
  4. Hubungkan ke komputer melalui kabel USB.
  5. Jalankan Aplikasi dari Android Studio melalui <Cmd+R> (OS X) atau <Ctrl+R> (Win, Linux)
  6. Arahkan ke Roda -> Utilitas Developer -> Jaringan Thread
  7. Lakukan interaksi dengan berbagai opsi yang tersedia. Di bagian di bawah ini, kita akan mengekstrak kode yang dieksekusi pada setiap tombol.

Apakah kredensial pilihan tersedia?

Pertanyaan pertama yang harus diajukan produsen TBR kepada Google adalah apakah rangkaian kredensial pilihan sudah ada di perangkat. Langkah ini harus menjadi titik awal alur Anda. Kode di bawah mengkueri GPS terkait keadaan kredensial. Perintah ini tidak meminta izin pengguna karena tidak ada kredensial yang dibagikan.

/**
* Prompts whether credentials exist in storage or not. Consent from user is not necessary
*/

fun doGPSPreferredCredsExist(activity: FragmentActivity) {
 try {
   // Uses the ThreadNetwork interface for the preferred credentials, adding
   // a listener that will receive an intentSenderResult. If that is NULL, 
   // preferred credentials don't exist. If that isn't NULL, they exist.
   // In this case we'll not use it.

   ThreadNetwork.getClient(activity).preferredCredentials.addOnSuccessListener { intentSenderResult ->
     intentSenderResult.intentSender?.let { intentSender ->
       ToastTimber.d("threadClient: preferred credentials exist", activity)
       // don't post the intent on `threadClientIntentSender` as we do when
       // we really want to know which are the credentials. That will prompt a
       // user consent. In this case we just want to know whether they exist
     } ?: ToastTimber.d(
       "threadClient: no preferred credentials found, or no thread module found", activity
     )
   }.addOnFailureListener { e: Exception ->
     Timber.d("ERROR: [${e}]")
   }
 } catch (e: Exception) {
   ToastTimber.e("Error $e", activity)
 }
}

Mendapatkan kredensial pilihan GPS

Jika ada, sebaiknya Anda membaca kredensialnya. Satu-satunya perbedaan dengan kode sebelumnya adalah setelah menerima intentSenderResult, Anda ingin membuat dan meluncurkan intent menggunakan hasil tersebut dari pengirim.

Dalam kode kita, untuk tujuan organisasi/arsitektur, kita menggunakan MutableLiveData<IntentSender?> karena kode aslinya berada di ViewModel (ThreadViewModel.kt) dan observer intent berada di Activity Fragment ( ThreadFragment.kt). Dengan demikian, setelah intentSenderResult diposting ke data langsung, kita akan mengeksekusi konten observer ini:

viewModel.threadClientIntentSender.observe(viewLifecycleOwner) { sender ->
 Timber.d(
   "threadClient: intent observe is called with [${intentSenderToString(sender)}]"
 )
 if (sender != null) {
   Timber.d("threadClient: Launch GPS activity to get ThreadClient")
   threadClientLauncher.launch(IntentSenderRequest.Builder(sender).build())
   viewModel.consumeThreadClientIntentSender()
 }
}

Tindakan ini akan memicu izin pengguna dengan kredensial berbagi, dan, jika disetujui, akan menampilkan konten melalui:

threadClientLauncher =
 registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
   if (result.resultCode == RESULT_OK) {
     val threadNetworkCredentials =
       ThreadNetworkCredentials.fromIntentSenderResultData(result.data!!)
     viewModel.threadPreferredCredentialsOperationalDataset.postValue(
       threadNetworkCredentials
     )
   } else {
     val error = "User denied request."
     Timber.d(error)
     updateThreadInfo(null, "")
   }
 }

Postingan kredensial ke MutableLiveData<ThreadNetworkCredentials?> dijelaskan di bawah.

Menetapkan kredensial GPS

Baik itu ada atau tidak, Anda harus mendaftarkan TBR Anda ke Layanan Google Play. Aplikasi Anda akan menjadi satu-satunya yang dapat membaca kredensial yang terkait dengan ID Agen Perbatasan TBR Anda, tetapi jika TBR Anda adalah yang pertama mendaftar, kredensial tersebut akan disalin ke set Kredensial yang Disukai. Informasi tersebut bisa diakses oleh semua Aplikasi pada ponsel, selama pengguna memberikan otorisasi.

/**
* Last step in setting the GPS thread credentials of a TBR
*/
private fun associateGPSThreadCredentialsToThreadBorderRouterAgent(
 credentials: ThreadNetworkCredentials?,
 activity: FragmentActivity,
 threadBorderAgent: ThreadBorderAgent,
) {
 credentials?.let {
   ThreadNetwork.getClient(activity).addCredentials(threadBorderAgent, credentials)
     .addOnSuccessListener {
       ToastTimber.d("threadClient: Credentials added", activity)
     }.addOnFailureListener { e: Exception ->
       ToastTimber.e("threadClient: Error adding the new credentials: $e", activity)
     }
 }
}

Menetapkan kredensial ke Produk TBR

Bagian ini adalah milik setiap vendor, dan dalam codelab ini, kami menerapkannya melalui Server Istirahat HTTP DBUS+Python atau Server Istirahat HTTP native dari OTBR.

/**
* Creates credentials in the format used by the OTBR HTTP server. See its documentation in
* https://github.com/openthread/ot-br-posix/blob/main/src/rest/openapi.yaml#L215
*/
fun createJsonCredentialsObject(newCredentials: ThreadNetworkCredentials): JSONObject {
 val jsonTimestamp = JSONObject()
 jsonTimestamp.put("Seconds", System.currentTimeMillis() / 1000)
 jsonTimestamp.put("Ticks", 0)
 jsonTimestamp.put("Authoritative", false)

 val jsonQuery = JSONObject()
 jsonQuery.put(
   "ActiveDataset",
   BaseEncoding.base16().encode(newCredentials.activeOperationalDataset)
 )
 jsonQuery.put("PendingTimestamp", jsonTimestamp)
 // delay of committing the pending set into active set: 10000ms
 jsonQuery.put("Delay", 10000)

 Timber.d(jsonQuery.toString())

 return jsonQuery
}

//(...)

var response = OtbrHttpClient.createJsonHttpRequest(
 URL("http://$ipAddress:$otbrPort$otbrDatasetPendingEndpoint"),
 activity,
 OtbrHttpClient.Verbs.PUT,
 jsonQuery.toString()
)

Mendapatkan kredensial dari Produk TBR Anda

Seperti yang ditunjukkan sebelumnya, gunakan GET HTTP Verb untuk memperoleh kredensial dari TBR Anda. Lihat contoh skrip Python.

Mem-build dan Mengimpor

Saat membuat Aplikasi Android, Anda harus membuat perubahan pada manifes, build, dan impor untuk mendukung Modul Thread Layanan Google Play. Tiga cuplikan berikut merangkum sebagian besar penambahan.

Perlu diperhatikan bahwa aplikasi contoh kami terutama dibuat untuk commissioning Matter. Oleh karena itu, file Manifest dan Gradle-nya lebih kompleks daripada penambahan yang diperlukan hanya untuk menggunakan Thread Credentials.

Perubahan manifes

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    (...)
    <!-- usesCleartextTraffic needed for OTBR local unencrypted communication -->
    <!-- Not needed for Thread Module, only used for HTTP -->
    <uses-feature
    (...)
        android:usesCleartextTraffic="true">

    <application>
    (...)
    <!-- GPS automatically downloads scanner module when app is installed -->
    <!-- Not needed for Thread Module, only used for scanning QR Codes -->
    <meta-data
        android:name="com.google.mlkit.vision.DEPENDENCIES"
        android:value="barcode_ui"/>
    </application>
</manifest>

Build.gradle

// Thread Network
implementation 'com.google.android.gms:play-services-threadnetwork:16.0.0'
// Thread QR Code Scanning
implementation 'com.google.android.gms:play-services-code-scanner:16.0.0'
// Thread QR Code Generation
implementation 'com.journeyapps:zxing-android-embedded:4.1.0'
// Needed for using BaseEncoding class
implementation 'com.google.guava:guava:31.1-jre'

Impor yang Relevan

// Thread Network Module
import com.google.android.gms.threadnetwork.ThreadNetworkCredentials
import com.google.android.gms.threadnetwork.ThreadBorderAgent
import com.google.android.gms.threadnetwork.ThreadNetwork

// Conversion of credentials to/fro Base16 (hex)
import com.google.common.io.BaseEncoding

// HTTP
import java.io.BufferedInputStream
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.nio.charset.StandardCharsets

// Co-routines for HTTP calls
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch


// JSON
import org.json.JSONObject

// Logs
import timber.log.Timber

// mDNS/SD
import android.net.nsd.NsdServiceInfo

// QR Code reader / writer
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import com.google.zxing.BarcodeFormat
import com.google.zxing.MultiFormatWriter
import com.journeyapps.barcodescanner.BarcodeEncoder

5. Penemuan mDNS/SD

Aplikasi contoh kami menggunakan penemuan mDNS/SD untuk membuat daftar Router Pembatas Thread yang tersedia di jaringan, serta BAID-nya masing-masing.

Hal ini sangat membantu saat memasukkan informasi TBR Anda ke penyimpanan kredensial GPS. Namun, penggunaannya di luar cakupan codelab ini. Kami menggunakan library Penemuan Layanan Android NSDManager, dan kode sumber lengkapnya tersedia di Aplikasi Contoh, di ServiceDiscovery.kt.

6. Rangkuman

Setelah menerapkan panggilan ini atau menggunakan Aplikasi Contoh, Anda dapat sepenuhnya mengaktivasi RPi OTBR. Aplikasi Contoh kami menampilkan 8 tombol:

91979bf065e9673d.png

Urutan yang memungkinkan untuk orientasi TBR Anda adalah:

  1. Kueri apakah ada kredensial preferensial (biru, baris pertama)
  2. Bergantung pada jawabannya
  3. Dapatkan kredensial pilihan GPS (biru, baris ke-2)
  4. Tetapkan kredensial TBR di GPS (biru, baris ke-3) -> Pilih TBR Anda -> Buat Acak -> Masukkan nama jaringan -> Oke
  5. Setelah memiliki kredensial pilihan, setel kredensial tersebut ke OTBR Anda menggunakan Setel kredensial RPi OTBR, yang akan menerapkan kredensial tersebut ke kumpulan yang tertunda.

Default untuk aplikasi contoh adalah menggunakan penundaan 10 detik. Dengan demikian, setelah periode ini, kredensial RPi TBR Anda (dan node lain yang mungkin ada di jaringannya) akan dimigrasikan ke set data baru.

7. Kesimpulan

Dalam codelab ini, kita telah meng-clone aplikasi Android contoh dan menganalisis beberapa cuplikan kode yang menggunakan Layanan Google Play Thread Storage API. Kami menggunakan API tersebut untuk memiliki set data umum yang dapat kami aktifkan di RPi TBR, yang menampilkan TBR vendor.

Memiliki semua TBR pengguna di jaringan yang sama akan meningkatkan ketahanan dan jangkauan di Thread Network. Tindakan ini juga mencegah perjalanan pengguna yang cacat, yang membuat aplikasi tidak dapat mengaktivasi Perangkat Thread karena tidak memiliki akses ke kredensial.

Semoga codelab dan Aplikasi Contoh ini membantu Anda mendesain dan mengembangkan Aplikasi dan produk Thread Border Router.

8. Referensi

Ko-prosesor RCP

DBUS