SDK של רשת Thread ל-Android

ערכת ה-SDK של רשת Thread מספקת פונקציונליות שדומה למחזיק מפתחות דיגיטלי, ומאפשרת לאפליקציות ל-Android לשתף את פרטי הכניסה לרשת Thread עם Google Play Services. כך האפליקציות שלכם יכולות להגדיר כל מכשיר Thread מכל סביבה של בית חכם, בלי לחשוף ישירות את פרטי הכניסה ונתוני המשתמשים.

בעזרת כמה קריאות ל-API, אתם יכולים:

  1. מבקשים מ-Google Play Services את פרטי הכניסה המועדפים לרשת Thread.
  2. מגדירים Thread Border Router (TBR) חדשים ומוסיפים את פרטי הכניסה לרשת Thread ל-Google Play Services.
  3. אם כבר יש לכם TBR בשטח, אתם יכולים לבדוק אם הם נמצאים ברשת המועדפת ולהעביר אותם, אם צריך.TBR

יש כמה תהליכים שמשתמשים ומפתחים יכולים לעבור. במדריך הזה נסביר על רובם, וגם על תכונות חשובות אחרות ועל שימוש מומלץ.

מונחים חשובים ומושגים שקשורים ל-API

לפני שמתחילים, כדאי להבין את המונחים הבאים:

  • פרטי הכניסה לרשת Thread: קובץ בינארי של ערכי TLV של Thread שמקודד את שם הרשת של Thread, את מפתח הרשת ומאפיינים אחרים שנדרשים למכשיר Thread כדי להצטרף לרשת Thread נתונה.

  • פרטי הכניסה המועדפים לרשת של פרוטוקול Thread: פרטי הכניסה לרשת של פרוטוקול Thread שנבחרו באופן אוטומטי, שאפשר לשתף עם אפליקציות של ספקים שונים באמצעות ה-API של getPreferredCredentials.

  • מזהה סוכן גבול: מזהה ייחודי גלובלי של 16 בייטים למכשיר TBR. המזהה הזה נוצר ומנוהל על ידי ספקים של border router.

  • אפליקציית ההגדרה של TBR: זו אפליקציית Android שמשמשת להגדרת מכשירי TBR חדשים ולהוספת פרטי הכניסה לרשת Thread אל Google Play Services. האפליקציה שלכם היא הבעלים המורשה של פרטי הכניסה שנוספו ויש לה גישה אליהם.

הרבה ממשקי API של Thread Network מחזירים משימה שמושלמת באופן אסינכרוני. אפשר להשתמש ב-addOnSuccessListener וב-addOnFailureListener כדי לרשום קריאות חוזרות (callbacks) לקבלת התוצאה. מידע נוסף מופיע במאמרי העזרה בנושא משימות.

בעלות על פרטי כניסה ותחזוקה שלהם

האפליקציה שמוסיפה את פרטי הכניסה לרשת Thread הופכת לבעלים של פרטי הכניסה, ויש לה הרשאות מלאות לגשת אליהם. אם תנסו לגשת לפרטי כניסה שנוספו על ידי אפליקציות אחרות, תופיע הודעת השגיאה PERMISSION_DENIED.

כבעלי האפליקציה, מומלץ לעדכן את אחסון פרטי הכניסה ששמורים ב-Google Play Services כשמעדכנים את רשת TBR. כלומר, צריך להוסיף פרטי כניסה כשנדרש, לעדכן את פרטי הכניסה כשפרטי הכניסה של border router לרשת Thread משתנים, ולהסיר את פרטי הכניסה כשמסירים את TBR או מאפסים אותו להגדרות המקוריות.

גילוי של Border Agent

צריך לשמור את פרטי הכניסה עם מזהה של סוכן גבולות. צריך לוודא שאפליקציית ההגדרה של TBR יכולה לזהות את מזהי ה-Border Agent של מכשירי TBR.

מכשירי TBR צריכים להשתמש ב-mDNS כדי לפרסם מידע על רשת Thread, כולל שם הרשת, מזהה ה-PAN המורחב ומזהה ה-Border Agent. ערכי ה-txt התואמים למאפיינים האלה הם nn,‏ xp ו-id, בהתאמה.

ברשתות עם Google Thread Border Router (gTBR), שירות Google Play Services מקבל באופן אוטומטי את פרטי הכניסה לרשת Google Thread לשימוש.

שילוב ה-SDK באפליקציה ל-Android

כדי להתחיל, מבצעים את השלבים הבאים:

  1. פועלים לפי ההוראות שמפורטות במאמר הגדרת Google Play Services.

  2. מוסיפים את התלות ב-Google Play Services לקובץ build.gradle:

    implementation 'com.google.android.gms:play-services-threadnetwork:16.2.1'
    
  3. אופציונלי: מגדירים מחלקת נתונים של BorderAgent לאחסון מידע של TBR. נשתמש בנתונים האלה לאורך המדריך:

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

בהמשך נסביר על השלבים המומלצים להוספה ולניהול של פרטי כניסה מועדפים.

הגדרות חדשות של נתב גבולות לרשת Thread

לפני שיוצרים רשת חדשה לנתבי גבול חדשים, חשוב לנסות קודם להשתמש בפרטי הכניסה המועדפים לרשת. כך אפשר לוודא שמכשירי Thread מחוברים לרשת Thread אחת כשהדבר אפשרי.

קריאה ל-getPreferredCredentials מפעילה פעילות, ומבקשת מהמשתמשים לאשר את הבקשה לאחזור מהרשת. אם פרטי הכניסה לרשת אוחסנו במחזיק המפתחות הדיגיטלי של 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. אם מגדירים TBR חדש, מומלץ להפעיל את preferredCredentials ולהפעיל את הפעילות. הקריאה הזו תבטיח שה-TBR החדש ישתמש באותם פרטי כניסה שכבר מאוחסנים כמועדפים בטלפון, וכך תעודד את ההתכנסות של TBRs שונים לאותה רשת.

    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. אם תרחיש השימוש שלכם קשור להגדרת מכשירים שהם לא TBR, כמו מכשיר קצה חדש של Matter-over-Thread, מומלץ להשתמש ב-API של allActiveCredentials כדי לאחזר את פרטי הכניסה. הקריאה הזו תסרוק את ה-TBR שנמצאים ברשת המקומית, ולכן לא תחזיר פרטי כניסה שלא זמינים ב-TBR מקומי קיים.

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

יצירת רשת Thread חדשה

אם אין פרטי כניסה מועדפים לרשת Thread ואין פרטי כניסה פעילים לרשת Thread שזמינים ברשת Thread של המשתמש, אתם יכולים להשתמש ב-API של addCredentials כדי להוסיף פרטי כניסה ל-Google Play Services. כדי לעשות את זה, צריך ליצור ThreadBorderAgent ולספק אובייקט ThreadNetworkCredentials.

כדי ליצור רשת אקראית, מפעילים את newRandomizeBuilder:

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

כדי לציין את שם הרשת בפרוטוקול Thread:

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

הוספת פרטי כניסה

כדי שפרטי הכניסה לרשת Thread יהיו זמינים לספקים אחרים של Thread, אנחנו צריכים להוסיף אותם ל-Google Play Services. לפני שנוכל להוסיף את פרטי הכניסה החדשים, אנחנו צריכים לדעת לאיזה מכשיר TBR שייכת רשת 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}]") }
}

זיהוי והעברה של border router בשטח

אם יש לכם border router בשטח, אתם יכולים להשתמש ב-isPreferredCredentials כדי לקבוע אם ה-border router שייכים לרשת המועדפת. ה-API הזה לא מבקש מהמשתמש הרשאה, ובודק את פרטי הכניסה של border router מול מה שמאוחסן ב-Google Play Services.

הפונקציה isPreferredCredentials מחזירה את הערך 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 לפי קבוצת נתונים תפעולית

במקרים מסוימים, ב-TBR כבר מוגדרת רשת Thread, ואתם רוצים להוסיף את רשת Thread הזו ל-Google Play Services כדי לשתף אותה עם ספקים אחרים. אפשר ליצור מכונה של 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. אם הפעולה תצליח, תוכלו לקבל את השם, הערוץ ופרטים אחרים של רשת Thread. רשימה מלאה של המאפיינים מופיעה במאמר 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}]") }
    

פרטי הכניסה לרשת Thread לפי סוכן גבולות

מזהה Border Agent מזהה באופן ייחודי מכשיר TBR. כדי להשתמש ב-API של getCredentialsByBorderAgent, קודם צריך ליצור אובייקט ThreadBorderAgent ולהעביר את המזהה של Border Agent.

אחרי שיוצרים את האובייקט 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}]") }
}

פרטי הכניסה לרשת Thread לפי מזהה רשת אישית (PAN) מורחב

בדומה ל-getPreferredCredentials, אפשר גם לבקש מהמשתמש פרטי כניסה ממזהה רשת אישית (PAN) מורחב של TBR. ה-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 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}]") }
}

משאבים

מידע נוסף על Thread Network SDK זמין במאמרי העזרה של ה-API.