從上一篇GATT Profile 簡介中提到過,BLE 設備工做的第一步就是向外廣播數據。廣播數據中帶有設備相關的信息。本文主要說一下 BLE 的廣播中的數據的規範以及廣播包的解析。java
BLE 中有兩種角色 Central 和 Peripheral ,也就是中心設備和外圍設備。中心設備能夠主動鏈接外圍設備,外圍設備發送廣播或者被中心設備鏈接。外圍經過廣播被中心設備發現,廣播中帶有外圍設備自身的相關信息。安全
廣播包有兩種: 廣播包 (Advertising Data)和 響應包 (Scan Response),其中廣播包是每一個設備必須廣播的,而響應包是可選的。 數據包的格式以下圖所示(圖片來自官方 Spec):每一個包都是 31 字節,數據包中分爲有效數據(significant)和無效數據(non-significant)兩部分。ide
Len
,表示接下來的 Len
個字節是數據部分。數據部分的第一個字節表示數據的類型 AD Type ,剩下的 Len - 1
個字節是真正的數據 AD data 。其中 AD type 很是關鍵,決定了 AD Data 的數據表明的是什麼和怎麼解析,這個在後面會詳細講;全部的 AD type 的定義在文檔 Core Specification Supplement 中。 AD Type 包括以下類型:函數
Flags: TYPE = 0x01。這個數據用來標識設備 LE 物理鏈接的功能。DATA 是 0 到多個字節的 Flag 值,每一個 bit 上用 0 或者 1 來表示是否爲 True。若是有任何一個 bit 不爲 0,而且廣播包是可鏈接的,就必須包含此數據。各 bit 的定義以下:ui
Service UUID: 廣播數據中通常都會把設備支持的 GATT Service 廣播出來,用來告訴外面本設備所支持的 Service。有三種類型的 UUID:16 bit, 32bit, 128 bit。廣播中,每種類型類型有有兩個類別:完整和非完整的。這樣就共有 6 種 AD Type。spa
Local Name: 設備名字,DATA 是名字的字符串。 Local Name 能夠是設備的全名,也能夠是設備名字的縮寫,其中縮寫必須是全名的前面的若干字符。code
TX Power Level: TYPE = 0x0A,表示設備發送廣播包的信號強度。DATA 部分是一個字節,表示 -127 到 + 127 dBm。orm
帶外安全管理(Security Manager Out of Band):TYPE = 0x11。DATA 也是 Flag,每一個 bit 表示一個功能:對象
外設(Slave)鏈接間隔範圍:TYPE = 0x12。數據中定義了 Slave 最大和最小鏈接間隔,數據包含 4 個字節:blog
服務搜尋:外圍設備能夠要請中心設備提供相應的 Service。其數據定義和前面的 Service UUID 相似:
Service Data: Service 對應的數據。
公開目標地址:TYPE = 0x17,表示但願這個廣播包被指定的目標設備處理,此設備綁定了公開地址,DATA 是目標地址列表,每一個地址 6 字節。
隨機目標地址:TYPE = 0x18,定義和前一個相似,表示但願這個廣播包被指定的目標設備處理,此設備綁定了隨機地址,DATA 是目標地址列表,每一個地址 6 字節。
Appearance:TYPE = 0x19,DATA 是表示了設備的外觀。
廠商自定義數據: TYPE = 0xFF,廠商自定義的數據中,前兩個字節表示廠商 ID,剩下的是廠商本身按照需求添加,裏面的數據內容本身定義。
還有一些其餘的數據,我這裏就不一一列舉了,有須要的能夠從這個文檔查閱 Core Specification Supplement 。
在 Android 可使用 BluetoothAdapter
來發起掃描。基本用法以下:
BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { // 解析廣播數據 parseAdvData(scanRecord); } }; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 開始掃描設備 mBluetoothAdapter.startLeScan(mLeScanCallback); ... // 中止掃描設備 mBluetoothAdapter.stopLeScan(mLeScanCallback);
當掃描到設備之後,就會回調 onLeScan(...)
,這裏的參數 scanRecord
就是廣播數據,這裏同時包含 廣播數據 和 掃描相應數據 (若是有的話),因此長度通常就是 62 字節。
根據上一節的廣播數據格式的說明,能夠實現解析廣播數據函數 parseAdvData(scanRecord);
,下面的代碼實現瞭解析幾個我關心的數據:
public static ParsedAd parseData(byte[] adv_data) { ParsedAd parsedAd = new ParsedAd(); ByteBuffer buffer = ByteBuffer.wrap(adv_data).order(ByteOrder.LITTLE_ENDIAN); while (buffer.remaining() > 2) { byte length = buffer.get(); if (length == 0) break; byte type = buffer.get(); length -= 1; switch (type) { case 0x01: // Flags parsedAd.flags = buffer.get(); length--; break; case 0x02: // Partial list of 16-bit UUIDs case 0x03: // Complete list of 16-bit UUIDs case 0x14: // List of 16-bit Service Solicitation UUIDs while (length >= 2) { parsedAd.uuids.add(UUID.fromString(String.format( "%08x-0000-1000-8000-00805f9b34fb", buffer.getShort()))); length -= 2; } break; case 0x04: // Partial list of 32 bit service UUIDs case 0x05: // Complete list of 32 bit service UUIDs while (length >= 4) { parsedAd.uuids.add(UUID.fromString(String.format( "%08x-0000-1000-8000-00805f9b34fb", buffer.getInt()))); length -= 4; } break; case 0x06: // Partial list of 128-bit UUIDs case 0x07: // Complete list of 128-bit UUIDs case 0x15: // List of 128-bit Service Solicitation UUIDs while (length >= 16) { long lsb = buffer.getLong(); long msb = buffer.getLong(); parsedAd.uuids.add(new UUID(msb, lsb)); length -= 16; } break; case 0x08: // Short local device name case 0x09: // Complete local device name byte sb[] = new byte[length]; buffer.get(sb, 0, length); length = 0; parsedAd.localName = new String(sb).trim(); break;