חיפוש מכשירי Bluetooth

באמצעות BluetoothAdapter תוכלו למצוא מכשירים מרוחקים עם Bluetooth באמצעות חיפוש מכשירים או באמצעות שאילתה ברשימת המכשירים המותאמים.

לפני שמנסים למצוא מכשירים עם Bluetooth, צריך לוודא שיש לכם את ההרשאות המתאימות ל-Bluetooth ולהגדיר את האפליקציה ל-Bluetooth.

זיהוי מכשירים הוא תהליך סריקה שמחפש מכשירים שתומכים ב-Bluetooth באזור המקומי ומבקש מידע מסוים על כל אחד מהם. התהליך הזה נקרא לפעמים גילוי, שאילתה או סריקה. מכשיר Bluetooth בקרבת מקום מגיב לבקשת גילוי רק אם הוא נמצא כרגע במצב גלוי ומקבל בקשות מידע. אם המכשיר גלוי, הוא משיב לבקשת הגילוי על ידי שיתוף מידע מסוים, כמו שם המכשיר, הסוג שלו וכתובת ה-MAC הייחודית שלו. בעזרת המידע הזה, המכשיר שמבצע את תהליך הגילוי יכול לבחור להתחיל חיבור למכשיר שנתגלה.

מאחר שמכשירים שגלויים לכולם עלולים לחשוף מידע על המיקום של המשתמש, תהליך זיהוי המכשירים דורש גישה למיקום. אם משתמשים באפליקציה במכשיר עם Android מגרסה 8.0 (רמת API‏ 26) ואילך, מומלץ להשתמש ב-Companion Device Manager API במקום זאת. ה-API הזה מבצע זיהוי של מכשירים בשם האפליקציה, כך שהאפליקציה לא צריכה לבקש הרשאות מיקום.

אחרי שמתבצע חיבור למכשיר מרוחק בפעם הראשונה, מופיעה בקשה למתאימה באופן אוטומטי. כשמתבצע התאמה של מכשיר, המידע הבסיסי על המכשיר הזה – כמו השם, הכיתה וכתובת ה-MAC שלו – נשמר וניתן לקרוא אותו באמצעות ממשקי ה-API של Bluetooth. אפשר להשתמש בכתובת ה-MAC הידועה של מכשיר מרוחק כדי ליצור עמו חיבור בכל שלב, בלי לבצע חיפוש, בתנאי שהמכשיר עדיין נמצא בטווח.

חשוב לזכור שיש הבדל בין התאמה לבין חיבור:

  • התאמה היא מצב שבו שני מכשירים יודעים על קיומו של השני, יש להם מפתח קישור משותף שאפשר להשתמש בו לאימות והם יכולים ליצור ביניהם חיבור מוצפן.
  • מחוברים: המכשירים משתפים ערוץ RFCOMM כרגע ויכולים להעביר נתונים זה לזה. ב-APIs הנוכחיים של Bluetooth, צריך להתאים בין המכשירים כדי ליצור חיבור RFCOMM. ההתאמה מתבצעת באופן אוטומטי כשמפעילים חיבור מוצפן באמצעות ממשקי ה-API של Bluetooth.

בקטעים הבאים מוסבר איך למצוא מכשירים שמותאמים ואיך למצוא מכשירים חדשים באמצעות זיהוי מכשירים.

שליחת שאילתות למכשירים מותאמים

לפני שמבצעים חיפוש של מכשירים, כדאי לשלוח שאילתה על קבוצת המכשירים המותאמים כדי לבדוק אם המכשיר הרצוי כבר ידוע. כדי לעשות זאת, מפעילים את הפקודה getBondedDevices(). הפונקציה מחזירה קבוצה של אובייקטים מסוג BluetoothDevice שמייצגים מכשירים מותאמים. לדוגמה, אפשר לשלוח שאילתה לכל המכשירים המותאמים ולקבל את השם ואת כתובת ה-MAC של כל מכשיר, כפי שמתואר בקטע הקוד הבא:

Kotlin

val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
pairedDevices?.forEach { device ->
   val deviceName = device.name
   val deviceHardwareAddress = device.address // MAC address
}

Java

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
   // There are paired devices. Get the name and address of each paired device.
   for (BluetoothDevice device : pairedDevices) {
       String deviceName = device.getName();
       String deviceHardwareAddress = device.getAddress(); // MAC address
   }
}

כדי ליצור חיבור למכשיר Bluetooth, כל מה שצריך מהאובייקט המשויך BluetoothDevice הוא כתובת ה-MAC, שאפשר לאחזר באמצעות הקריאה getAddress(). מידע נוסף על יצירת חיבור זמין במאמר חיבור מכשירי Bluetooth.

איתור מכשירים

כדי להתחיל לאתר מכשירים, צריך להפעיל את הפונקציה startDiscovery(). התהליך הוא אסינכרוני ומחזיר ערך בוליאני שמציין אם תהליך הגילוי התחיל בהצלחה. תהליך הגילוי כולל בדרך כלל סריקה של חקירה למשך כ-12 שניות, ולאחר מכן סריקה של דף של כל מכשיר שנמצא כדי לאחזר את שם ה-Bluetooth שלו.

כדי לקבל מידע על כל מכשיר שזוהה, האפליקציה צריכה לרשום BroadcastReceiver לכוונה ACTION_FOUND. המערכת משדרת את הכוונה הזו לכל מכשיר. הכוונה מכילה את השדות הנוספים EXTRA_DEVICE ו-EXTRA_CLASS, שמכילים BluetoothDevice ו-BluetoothClass, בהתאמה. בקטע הקוד הבא מוסבר איך אפשר להירשם לטיפול בשידור כשמתגלים מכשירים:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
   ...

   // Register for broadcasts when a device is discovered.
   val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
   registerReceiver(receiver, filter)
}

// Create a BroadcastReceiver for ACTION_FOUND.
private val receiver = object : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       val action: String = intent.action
       when(action) {
           BluetoothDevice.ACTION_FOUND -> {
               // Discovery has found a device. Get the BluetoothDevice
               // object and its info from the Intent.
               val device: BluetoothDevice =
                       intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
               val deviceName = device.name
               val deviceHardwareAddress = device.address // MAC address
           }
       }
   }
}

override fun onDestroy() {
   super.onDestroy()
   ...

   // Don't forget to unregister the ACTION_FOUND receiver.
   unregisterReceiver(receiver)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
   ...

   // Register for broadcasts when a device is discovered.
   IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
   registerReceiver(receiver, filter);
}

// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
   public void onReceive(Context context, Intent intent) {
       String action = intent.getAction();
       if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           // Discovery has found a device. Get the BluetoothDevice
           // object and its info from the Intent.
           BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
           String deviceName = device.getName();
           String deviceHardwareAddress = device.getAddress(); // MAC address
       }
   }
};

@Override
protected void onDestroy() {
   super.onDestroy();
   ...

   // Don't forget to unregister the ACTION_FOUND receiver.
   unregisterReceiver(receiver);
}

כדי ליצור חיבור למכשיר Bluetooth, צריך להפעיל את getAddress() ב-BluetoothDevice כדי לאחזר את כתובת ה-MAC המשויכת.

הפעלת יכולת הגילוי

כדי שהמכשיר המקומי יהיה גלוי למכשירים אחרים, צריך להפעיל את הקריאה startActivityForResult(Intent, int) עם הכוונה ACTION_REQUEST_DISCOVERABLE. כך תישלח בקשה להפעלת מצב החשיפה של המערכת בלי שתצטרכו לנווט לאפליקציית ההגדרות, מה שיעצור את האפליקציה שלכם. כברירת מחדל, המכשיר יהיה גלוי למשך שתי דקות. אפשר להגדיר משך זמן אחר, עד חמש דקות, על ידי הוספת התוסף EXTRA_DISCOVERABLE_DURATION.

קטע הקוד הבא מגדיר שהמכשיר יהיה גלוי למשך חמש דקות:

Kotlin

val requestCode = 1;
val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
   putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivityForResult(discoverableIntent, requestCode)

Java

int requestCode = 1;
Intent discoverableIntent =
       new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent, requestCode);


איור 2: תיבת הדו-שיח להפעלת יכולת הגילוי.

תוצג תיבת דו-שיח שבה תתבקשו מהמשתמש להעניק הרשאה להפוך את המכשיר לגלוי, כפי שמוצג באיור 2. אם המשתמש יגיב 'אישור', המכשיר יהיה גלוי למשך פרק הזמן שצוין. לאחר מכן, הפעילות תקבל קריאה ל-callback‏ onActivityResult(), עם קוד התוצאה שווה למשך הזמן שבו המכשיר גלוי. אם המשתמש ענה 'דחייה' או אם התרחשה שגיאה, קוד התוצאה הוא RESULT_CANCELED.

המכשיר יישאר במצב גלוי בשקט למשך הזמן שהוקצה. כדי לקבל התראה כשהמצב הגלוי השתנה, צריך לרשום BroadcastReceiver לכוונה ACTION_SCAN_MODE_CHANGED. הכוונה הזו מכילה את השדות הנוספים EXTRA_SCAN_MODE ו-EXTRA_PREVIOUS_SCAN_MODE, שמספקים את אופן הסריקה החדש והישן, בהתאמה. הערכים האפשריים לכל תוספת הם:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
המכשיר במצב גלוי.
SCAN_MODE_CONNECTABLE
המכשיר לא במצב גלוי, אבל עדיין אפשר להתחבר אליו.
SCAN_MODE_NONE
המכשיר לא נמצא במצב גלוי ולא ניתן לקבל בו חיבורים.

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