1. قبل از شروع
در آزمایشگاه کد Thread Border Router (TBR) ، نحوه ساخت یک Thread Border Router بر اساس Raspberry Pi را نشان می دهیم. در آن کد لبه ما
- اتصال IP دوطرفه را بین شبکه های Thread و Wi-Fi/Ethernet برقرار کنید.
- کشف سرویس دو جهته را از طریق mDNS (در پیوند Wi-Fi/Ethernet) و SRP (در شبکه Thread) ارائه دهید.
این کد لبه بر روی مورد قبلی ساخته شده است و به این موضوع می پردازد که چگونه روتر مرزی شما و برنامه شما می توانند با API های Google برای ایجاد یک شبکه موضوعی واحد تعامل داشته باشند. اعتبارنامه های همگرا Thread مهم است زیرا به استحکام شبکه می افزاید و تعامل کاربر با برنامه هایی که به Thread متکی هستند را ساده می کند.
پیش نیازها
- OTBR Codelab را کامل کنید
- دانش اولیه لینوکس، اندروید/کاتلین و شبکه های Thread
چیزی که یاد خواهید گرفت
- نحوه استفاده از Thread Sharing API ها برای دریافت و تنظیم مجموعه های اعتبار
- چگونه روتر مرزی OpenThread خود را با اعتباری مشابه شبکه Google تنظیم کنید
آنچه شما نیاز دارید
- برد Raspberry Pi 4 یا یک برد دیگر مبتنی بر لینوکس که از Open Thread Border Router (OTBR) استفاده می کند.
- بردی که اتصال IEEE 802.15.4 را به عنوان یک پردازشگر رادیویی (RCP) فراهم می کند. فهرستی از مخازن فروشندگان مختلف SoC و دستورالعمل های آنها را در صفحه OpenThread Github مشاهده کنید.
2. راه اندازی سرویس HTTP
اولین بلوک ساختمانی که ما به آن نیاز داریم، رابطی است که به ما امکان میدهد اعتبارنامههای فعال را بخوانیم و اعتبارنامههای در انتظار را در OTBR خود بنویسیم. هنگام ساخت یک TBR، همانطور که در اینجا با دو مثال نشان داده شده است، از مکانیسم های اختصاصی خود استفاده کنید. گزینه اول نحوه ارتباط با عامل OTBR به صورت محلی از طریق DBUS را نشان می دهد، در حالی که گزینه دوم از Rest API استفاده می کند که می تواند بر روی OTBR ساخته شود.
هیچیک از این روشها ایمن نیستند و نباید آنطور که در محیط تولید وجود دارد استفاده شوند. با این حال، یک فروشنده می تواند برای استفاده از آن در یک محیط تولید، رمزگذاری را در اطراف هر یک از روش ها ایجاد کند، یا می توانید سرویس مانیتور خود را برای صدور HTTP حلقه بک یا تماس های ذاتاً محلی DBUS گسترش دهید.
گزینه 1: DBUS و HTTP API در Python Script
این مرحله یک سرویس HTTP بدون استخوان ایجاد می کند که دو نقطه پایانی را در معرض خواندن و تنظیم اعتبار قرار می دهد و در نهایت دستورات DBUS را فراخوانی می کند.
در RPi که به عنوان OTBR شما خدمت می کند، وابستگی های Python 3 را نصب کنید:
$ pip install dbus-python shlex json
اسکریپت را به صورت زیر اجرا کنید:
$ sudo python credentials_server.py 8081 serving at port 8081
نمونه یک سرور HTTP را در پورت 8081 راه اندازی می کند و در مسیر ریشه به درخواست GET برای بازیابی اعتبار موضوع یا یک درخواست POST برای تنظیم اعتبار Thread گوش می دهد. محموله همیشه یک ساختار JSON با TLV است.
درخواست PUT زیر از طریق مسیر /node/dataset/pending
اعتبارنامه موضوعات در انتظار جدید را به OTBR تنظیم می کند. در این حالت، اعتبارنامه های معلق در 10 ثانیه اعمال می شوند:
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 }
یک درخواست GET برای /node/dataset/active
اعتبارنامه فعال فعلی را واکشی می کند.
GET /node/dataset/active Host: <IP>:8081 ContentType = "application/json" acceptMimeType = "text/plain" ... <TLV encoded Thread Dataset>
اسکریپت دستورات DBUS R/W را به مسیر گذرگاه io.openthread.BorderRouter.wpan0
، مسیر شی /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 امکان بررسی قابلیت های خود را فراهم می کند. می توانید این کار را به صورت زیر انجام دهید:
$ sudo dbus-send --system --dest=io.openthread.BorderRouter.wpan0 \ --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 \ org.freedesktop.DBus.Introspectable.Introspect
همچنین می توانید قابلیت های پشتیبانی شده را که در اینجا مستند شده است بررسی کنید.
گزینه 2: OTBR Agent Native HTTP Rest API
روتر مرزی OpenThread به طور پیش فرض با پرچم REST_API=1
ساخته می شود و REST API را فعال می کند. در صورتی که ساخت شما از یک Codelab قبلی REST API را فعال نکرده باشد، مطمئن شوید که OTBR را روی RPi خود با آن پرچم بسازید:
$ REST_API=1 INFRA_IF_NAME=wlan0 ./script/setup
یک عامل OTBR را می توان با اجرای:
$ sudo systemctl restart otbr-agent.service
عامل یک سرور HTTP را در پورت 8081 راه اندازی می کند. این سرور به کاربر یا برنامه مانیتور اجازه می دهد تا بسیاری از وظایف را در OTBR انجام دهد ( در اینجا مستند شده است). می توانید از مرورگر، curl
یا wget
خود برای بررسی محتویات آن استفاده کنید. در میان بسیاری از مسیرهای پشتیبانی شده، موارد استفاده شرح داده شده در بالا، با فعل GET
در /node/dataset/active
و فعل PUT
در /node/dataset/pending
3. راه اندازی Credential Framework در اندروید
اعتبار ترجیحی
خدمات Google Play در Android اجازه می دهد و انتظار دارد که اعتبارنامه ها برای همه TBR ها در شبکه شما ثبت شود. هر کدام با شناسه عامل مسیریاب مرزی خود (BAID) شناسایی می شوند. برای انجام این کار از متد addCredentials()
رابط ThreadNetworkClient
استفاده خواهید کرد. اولین TBR که به فضای ذخیرهسازی خدمات Google Play اضافه میشود، اعتبار ترجیحی این دستگاه تلفن همراه را تعیین میکند.
برنامه ای که مجموعه ای از اعتبارنامه های شبکه Thread را به BAID خود اضافه می کند، مالک اعتبارنامه ها می شود و مجوزهای کامل برای دسترسی به آنها را دارد. اگر سعی کنید به اطلاعات کاربری اضافه شده توسط برنامه های دیگر دسترسی پیدا کنید، یک خطای PERMISSION_DENIED دریافت خواهید کرد. با این حال، اعتبار ترجیحی همیشه برای هر برنامه با رضایت کاربر در دسترس است. توصیه می کنیم زمانی که شبکه Thread Border Router به روز می شود، اعتبارنامه هایی را که در سرویس های Google Play ذخیره می شوند، به روز نگه دارید. اگرچه امروز از این اطلاعات استفاده نمیشود، ممکن است در آینده سفرهای پیشرفتهتری ارائه کنیم.
حتی اگر اولین TBR بعداً حذف شود، اعتبارنامه ترجیحی در دستگاه Android باقی خواهد ماند. پس از تنظیم، سایر برنامههایی که اعتبار Thread را مدیریت میکنند، ممکن است اعتبارنامهها را از یک تماس getPreferredCredentials()
دریافت کنند.
Google TBR Sync
دستگاههای Android بهطور خودکار با Google TBR همگامسازی میشوند. اگر هیچ اعتباری در Android وجود نداشته باشد، دستگاهها آنها را از Google TBR در شبکه شما استخراج میکنند و این اعتبارنامهها به اعتبارنامه ترجیحی تبدیل میشوند. همگامسازی بین TBR و دستگاه Android تنها در صورتی انجام میشود که TBR با یک کاربر جفت شود، یا اگر با دو کاربر که در یک خانه هوشمند ( ساختار ) هستند جفت شود.
این فرآیند همچنین زمانی اتفاق میافتد که کاربر دیگری از Google در GHA برای Android یا GHA برای iOS باشد، زمانی که کاربر در همان ساختار باشد. در مورد GHA برای iOS، اگر اعتبار ترجیحی وجود نداشته باشد، اطلاعات کاربری ترجیحی در فضای ذخیره سازی iOS تنظیم می شود.
اگر دو دستگاه Android (یا Android + iGHA) در یک شبکه با مجموعههای مختلف اعتبار ترجیحی وجود داشته باشند، دستگاهی که در ابتدا TBR را پیکربندی کرده است بر TBR غالب خواهد بود.
نصب TBR شخص ثالث
فضای ذخیرهسازی اعتبار در حال حاضر توسط خانه هوشمند کاربر ( ساختار ) نیست. هر دستگاه اندرویدی حافظه BAID خود را خواهد داشت، اما هنگامی که یک Google TBR در شبکه وجود دارد، سایر دستگاههای Android و دستگاههای iOS که برنامه Google Home برای iOS را اجرا میکنند با آن TBR همگامسازی میشوند و سعی میکنند اعتبار محلی را در حافظه تلفن تنظیم کنند.
قبل از اینکه یک OOB TBR جدید یک شبکه ایجاد کند، مهم است که بررسی کنید آیا یک شبکه ترجیحی از قبل در حافظه Android وجود دارد یا خیر.
- اگر شبکه ترجیحی وجود داشته باشد، فروشنده باید از آن استفاده کند. این تضمین می کند که دستگاه های Thread در صورت امکان به یک شبکه Thread متصل هستند.
- وقتی شبکه ترجیحی وجود ندارد، یک مجموعه اعتبارنامه جدید ایجاد کنید و آن را به TBR خود در خدمات Google Play اختصاص دهید. Android این اعتبارنامهها را بهعنوان اعتبارنامههای استاندارد تنظیمشده در همه TBRهای مبتنی بر Google مورد احترام قرار میدهد و سایر فروشندگان میتوانند با دستگاههای اضافی دسترسی و استحکام مش شما را افزایش دهند.
4. شبیه سازی و اصلاح برنامه اندرویدی خود
ما یک برنامه Android ایجاد کردهایم که تماسهای اصلی ممکن به Thread API را نشان میدهد. می توانید از این الگوها در برنامه خود استفاده کنید. در این لبه کد، ما برنامه Google Home Sample برای Matter را از Github شبیه سازی می کنیم.
تمام کد منبع نشان داده شده در اینجا قبلاً در برنامه نمونه کدگذاری شده است. از شما دعوت شده است که آن را مطابق با نیازهای خود تغییر دهید، اما می توانید به سادگی برنامه را شبیه سازی کنید یا باینری های از پیش ساخته شده را برای بررسی عملکرد اجرا کنید.
- با استفاده از:
$ git clone https://github.com/google-home/sample-apps-for-matter-android.git
- Android Studio را دانلود و باز کنید.
- روی File > Open کلیک کنید و به مخزن کلون شده خود اشاره کنید.
- حالت توسعه دهنده را در گوشی اندرویدی خود فعال کنید .
- آن را از طریق کابل USB به کامپیوتر خود وصل کنید.
- برنامه را از Android Studio از طریق <Cmd+R> (OS X) یا <Ctrl+R> (Win، Linux) اجرا کنید.
- به Wheel -> Developer Utilities -> Thread Network بروید
- با گزینه های مختلف موجود تعامل داشته باشید. در بخشهای زیر، کدی را که روی هر دکمه اجرا میشود، باز میکنیم.
آیا مدارک ترجیحی وجود دارد؟
اولین سوالی که یک سازنده TBR باید از گوگل بپرسد این است که آیا مجموعه ای از اعتبارنامه های ترجیحی از قبل در دستگاه وجود دارد یا خیر. این باید نقطه شروع جریان شما باشد. کد زیر از GPS در مورد وجود اعتبارنامه سؤال می کند. رضایت کاربر را درخواست نمی کند زیرا هیچ اعتباری به اشتراک گذاشته نمی شود.
/** * 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) } }
دریافت مدارک ترجیحی GPS
در صورت وجود آنها، می خواهید اعتبارنامه ها را بخوانید. تنها تفاوت با کد قبلی این است که پس از دریافت intentSenderResult
، میخواهید با استفاده از نتیجه فرستنده یک intent بسازید و راهاندازی کنید.
در کد ما، برای اهداف سازمانی/معماری، از MutableLiveData<IntentSender?>
استفاده می کنیم، زیرا کد اصلی در ViewModel (ThreadViewModel.kt) و ناظرهای هدف در Activity Fragment ( ThreadFragment.kt ) هستند. بنابراین، هنگامی که intentSenderResult به دادههای زنده ارسال شد، محتوای این مشاهدهگر را اجرا میکنیم:
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() } }
این باعث رضایت کاربر با اشتراکگذاری اعتبار میشود و در صورت تایید، محتوا را از طریق:
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, "") } }
ارسال اعتبار به MutableLiveData<ThreadNetworkCredentials?>
در زیر توضیح داده شده است.
تنظیم اعتبار GPS
خواه وجود داشته باشند یا نباشند، باید TBR خود را در خدمات Google Play ثبت کنید. برنامه شما تنها اپلیکیشنی خواهد بود که میتواند اعتبارنامههای مرتبط با Border Agent ID TBR شما را بخواند، اما اگر TBR شما اولین نفری باشد که ثبتنام میکند، آن اعتبارنامهها در مجموعه اعتبارنامههای ترجیحی کپی میشوند. این اطلاعات تا زمانی که کاربر آن را مجاز کرده باشد، برای هر برنامه تلفنی قابل دسترسی است.
/** * 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) } } }
تنظیم اعتبار برای محصول TBR شما
این بخش اختصاصی هر فروشنده است و در این کد لبه ما آن را از طریق DBUS+Python HTTP Rest Server یا HTTP Rest Server بومی از 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() )
دریافت اعتبار از محصول TBR شما
همانطور که قبلا نشان داده شد، از GET HTTP Verb برای به دست آوردن اعتبار از TBR خود استفاده کنید. نمونه اسکریپت پایتون را ببینید.
ساخت و واردات
هنگام ایجاد برنامه Android خود، باید تغییراتی در مانیفست، ساخت و وارد کردن خود ایجاد کنید تا از ماژول موضوع Google Play Services پشتیبانی کنید. سه قطعه زیر بیشتر موارد اضافه شده را خلاصه می کند.
توجه داشته باشید که برنامه نمونه ما در درجه اول برای راه اندازی Matter ساخته شده است. بنابراین، فایلهای Manifest و Gradle آن پیچیدهتر از اضافات لازم برای استفاده از Thread Credentials هستند.
تغییرات آشکار
<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'
واردات مربوطه
// 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. mDNS/SD Discovery
برنامه نمونه ما از کشف mDNS/SD برای ایجاد لیستی از Thread Border Routerهای موجود در شبکه و همچنین BAIDهای مربوطه آنها استفاده می کند.
این هنگام وارد کردن اطلاعات TBR خود در فضای ذخیره سازی اعتبار GPS بسیار مفید است. با این حال، استفاده از آن فراتر از محدوده این Codelab است. ما از کتابخانه Android Service Discovery NSDManager استفاده می کنیم و کد منبع کامل در برنامه Sample در ServiceDiscovery.kt
موجود است.
6. کنار هم قرار دادن همه چیز
هنگامی که این تماس ها را اجرا کردید یا از برنامه نمونه استفاده کردید، می توانید به طور کامل از RPi OTBR خود استفاده کنید. برنامه نمونه ما 8 دکمه را نشان می دهد:
یک توالی ممکن برای نصب TBR شما به صورت زیر است:
- پرس و جو کنید که آیا اعتبارنامه ترجیحی وجود دارد (آبی، ردیف 1)
- بسته به جواب
- دریافت اطلاعات کاربری ترجیحی GPS (آبی، ردیف دوم)
- اعتبارنامه TBR را در GPS تنظیم کنید (آبی، ردیف 3) -> TBR خود را انتخاب کنید -> ایجاد تصادفی -> نام شبکه را وارد کنید -> Ok
- اکنون که اعتبارنامه های ترجیحی را دارید، آنها را با استفاده از Set RPi OTBR credentials روی OTBR خود تنظیم کنید، که این اعتبارنامه ها را در مجموعه در انتظار اعمال می کند.
پیش فرض برای برنامه نمونه استفاده از تأخیر 10 ثانیه است. بنابراین پس از این مدت، اعتبار RPi TBR شما (و سایر گره هایی که ممکن است در شبکه آن وجود داشته باشد) به مجموعه داده جدید منتقل می شود.
7. نتیجه گیری
در این نرمافزار، ما یک نمونه برنامه اندروید را شبیهسازی کردیم و چند قطعه کد را که از APIهای Thread Storage سرویسهای Google Play استفاده میکنند، تجزیه و تحلیل کردیم. ما از آن APIها برای داشتن یک مجموعه داده مشترک استفاده کردیم که بتوانیم آن را روی یک TBR RPi، که TBR یک فروشنده را به نمایش میگذارد، نصب کنیم.
داشتن تمام TBR کاربر در یک شبکه، انعطاف پذیری و دسترسی شبکه Thread را بهبود می بخشد. همچنین از سفرهای کاربر معیوب که در آن برنامهها نمیتوانند روی دستگاههای Thread Devices نصب شوند، زیرا به اعتبارنامه دسترسی ندارند، جلوگیری میکند.
ما امیدواریم که این نرم افزار کد و نمونه برنامه به شما در طراحی و توسعه اپلیکیشن و محصول Thread Border Router خود کمک کند.
8. مراجع
پردازنده کمکی RCP
DBUS