這裏只列出重點原理內容,更加細節的內容請閱讀前面文章
java
首先要搞清楚一點,咱們在 Android 中經過 SDK 得到的藍牙廣播包是通過底層的 SDK 給咱們處理過的,是一個長度爲 62 的字節數組。這個長度爲 62 的字節數組是怎麼來的呢?數組
想要搞清楚這個問題,首先咱們要明白 iBeacon
向外發送的最原始的廣播包是什麼樣的?markdown
首先咱們要搞清楚一點,藍牙在向外發送數據的時候是分紅兩個部分的一個就是普通的廣播包還有一個叫作應答包。這是藍牙協議的規定內容,針對於全部的藍牙設備(iBeacon 只是藍牙設備的一種)oop
普通的廣播包格式是定義好的,長度爲 30 byte
ui
應答包中的內容是能夠由 藍牙的各個製造廠商本身向裏面放數據的。最大長度是 32 byte
spa
須要注意的是,發送數據是從低位到高位一次發送,因此接收到的數據要返回來按字節拼接,例如接收到的MAC爲 8b 03 00 b0 01 c2,那麼實際的MAC爲 c2:01:b0:00:03:8b翻譯
首先咱們來看一下第一個藍牙廣播包(來自 iBeacon 設備),一共 59 個字節3d
04 3e 38 0d 01 13 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e 29個字節
02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5 30個字節
複製代碼
第一行的內容能夠認爲是藍牙廣播包中的附帶信息,經過 Android SDK 是無法看到的,第二行是對應咱們 Android SDK 中收到的廣播包中的前一部分。code
第一個字節是HCI Packet Type,04表示這是HCI Event;剩下的58bytes則是HCI Event的具體內容
第二個字節是EventCode,3e是此事件的代碼;第三個字節是Parameter Length,0x38(十進制56)表示後面數據長度56bytes
第四個字節是SubEvent,0d表示這是LE Extended Advertising Report;第五個字節是Num Reports,數值爲01
1b 00這兩個字節表明Event Type,因爲發送數據都是按字節發送以及從低位向高位發送,所以真實值是 001b
01 表示這是隨機設備地址
8b 03 00 b0 01 c2 是此設備的MAC,根據從低向高的發送規則,因此真實MAC是 c2:01:b0:00:03:8b
01 表明首要廣播信道的帶寬
00 表明次要廣播信道的帶寬,此處表示不使用次要信道
ff 表示廣播SID
7f 表明Tx Power的大小,此處是127dbm
af 表明RSSI的大小,此處是-81dbm
00 00 表明週期廣播間隔
00 表明直接地址類型,次數是公共設備地址
00 00 00 00 00 00 表明直接BD_ADDR
1e 表明接下的的數據的字節數(長度),如下數據就是最重要的廣播數據了
複製代碼
上面的內容就是對應第一行的解釋了,其實 Android SDK 已經幫咱們把這些數據中的部份內容解析出來,咱們能夠直接經過對應的 SDK 的方法來直接獲取。orm
下面咱們再來看 真正意義上的廣播包
格式是這樣的:
一個廣播包是由若干個廣播單元 AD Structure 構成的。每一個 AD Structure 的組成是:第一個字節表示長度值 length,表示接下來的 length 個字節是數據部分,數據部分的第一個字節表示數據的類型 AD Type,AD Type 決定了下面的數據表明了什麼,關於每一個數值表明的數據類型見官方文檔,剩下的 length - 1 個字節表示真正的數據
02 01 06
02 表示接下來的數據有兩個字節 01 表示數據類型,此處類型是 Flags 06 就是具體的數值了 0x06 = 0000 0110 每一位都有不一樣的含義,見官方文檔
複製代碼
1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5
1a 表示接下來的數據有 26 字節
FF 表示數據類型,此處類型是 廠商自定義數據類型(這裏的廠商指的是蘋果公司,由於 iBeacon 是蘋果公司提出的)
4C 00 表示公司的 ID,此處的 004C 表明蘋果公司
02 15 Beacon 的標識位,必須是這樣的
fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25
表示 Beacon UUID
27 11 是 major 的值
4C b9 是 minor 的值
C5 表示 Measured Power 表示的是此設備在 1 米處的 RSSI 值,用於距離測算
複製代碼
這段內容其實主要是蘋果公司在藍牙協議的基礎上定義的。
若是符合 1AFF4C000215 則說明此設備是 iBeacon
設備
04 3e 38 0d 01 1b 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e 29個字節
02 0a 00 08 16 f0 ff 64 27 11 4c b9 11 09 4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37 30個字節
複製代碼
其中第一行與上面同樣,這裏再也不介紹
02 0a 00
02 表示接下來的數據長度 2 個字節
0a 表示數據類型 這裏表示 Tx Power Level 取值範圍是 -127 到 127 dBm
00 表示 0 dBm
08 16 f0 ff 64 27 11 4c b9
08 表示數據長度
16 表示 Service Data 由 Service UUID 和 service 數據組成 前兩個字節是 UUID 後面是數據
f0ff 是 Service UUID
64 27 11 4c b9 是數據
11 09 4d 69 6e 69 42 65 61 63 6f 6e
11 表示數據長度
09 表示設備完整的名字
4d 69 6e 69 42 65 61 63 6f 6e 就是設備名字的 ASSIC 碼了 對應 MiniBeacon
M i n i B e a c o n
5f 30 30 39 30 37
這幾個數據就是 Beacon 開發者隨便亂加入的數據了,不符合協議內容
複製代碼
上面咱們分別分析了藍牙原始數據包中的廣播包和應答包,其實對於 iBeacon
來講廣播包中的大多數內容其肯定的,只有 UUID Major Minor
會有變化。並且每一個位置所表明的做用都已經被 蘋果公司
定義好了。若是想要 iBeacon
發出的數據包有更多的內容,那麼咱們就能夠在應答包中作文章了,應答包是有 32 個字節的。咱們只須要按照協議的內容嚮應答包中添加數據就能夠了。
對於 Android 客戶端,經過 Scanresult.getScanRecord().getBytes()
得到的廣播包是 62
個字節,它把上面原始數據包中的內容提取出來了,只保留了第二行內容。就是 藍牙廣播包第二行(30 byte) + 藍牙應答包第二行(最多 32 byte,數目不肯定),若是位數不夠的話就用 0 補充。
因此咱們如今就能夠很好根據得到的 byte[] 數組來解析廣播包了。
// 如今就得到廣播包了
byte[] result = ScanResult.getScanRecord().getBytes();
// UUID 包含 result[9] 和 result[24]
result[9]---result[24];
// Major
result[25] result[26]
// Minor
result[27] result[28]
// Measured Power
result[29]
// 通常咱們都是直接會先把 廣播包轉成 16 進制的格式而後來截取
String uuid = broadcast.substring(18, 50);
// 至於後面應答包的內容就要根據具體的廣播包格式來進行解析了,好比大家公司的硬件開發人員把電池電量放入了裏面,那麼大家就約定好放在什麼位置,到時候你直接取就能夠了。
複製代碼
這幾個方法所得到內容都不是直接從 Android 中收到的廣播(ScanResult.getScanRecord().getBytes())中解析出來的,而是從原始數據包中解析的。
getTxPower
獲取傳輸功率,若是這個 iBeacon 不支持的話,那麼結果就是 127
後面這幾個方法做用不大,關鍵看設備是否支持
關鍵方法
ScanRecord
中的這幾個方法就很重要的,這幾個方法都和咱們收到的廣播包有關係。
好比:若是應答包中對 Tx Power Level
進行了設置咱們就能夠經過 getTxPowerLevel()
來直接獲取。好比上面例子中的廣播包,經過調用方法 getTxPowerLevel()
就能夠獲得 0
其餘方法相似,只要你的應答包中數據的格式正確,就能夠解析出來。
舉例說明:
好比 Android 端收到的廣播包是:
0201061AFF4C0002150123456789ABCDEF0123456789ABCDEF00000007C5 廣播包
020A00 0303F1FF 0E16F1FF6400000007AC233F66C401 070965526F7574650000 應答包
複製代碼
getTxPowerLevel()
返回 0 由於在應答包中有正確的格式數據 020A00
getServiceData()
也會返回值,由於在應答包中有對應的數據 0E16F1FF6400000007AC233F66C401
0E 表示數據長度
16 表示類型 此處表示 Service Data - 16-bit UUID (不只僅是 UUID 還帶有數據) 前兩個字節表示 UUID 後面是數據
F1FF 表示 UUID
6400000007AC233F66C401 表示數據
Map<ParcelUuid, byte[]> getServiceData() 返回的值就是用 UUID 和 數據做爲鍵值對的形式
此處返回的 Map 集合中的內容是 注意:變化的 UUID 其他位數不會變化,若是廣播包中 UUID 不是 F1FF,那麼只須要對應替換就能夠了
ParcelUuid = ParcelUuid.fromString("0000fff1-0000-1000-8000-00805f9b34fb");
byte[] 就是數據部分對應的字節數值
複製代碼
List<ParcelUuid> getServiceUuids()
方法對應的就是應答包中的數據 0303F1FF
因爲只出現一次,因此 list 的 size就只有一個就是 F1FF
對應的 ParcelUuid 就是 ParcelUuid.fromString("0000fff1-0000-1000-8000-00805f9b34fb");
一樣的下面幾個方法也是對 Android 端收到的 62 byte 的廣播包中數據的解析所得
String getDeviceName()
得到是名字 須要廣播包中有對應的數據 070965526F7574650000
SpareArray<byte[]> getManufacturerSpecificaData()
獲取的製造商的數據,對應 4C000215
byte[] getManufacturerSpecificData(int manufacture)
根據製造商代碼(4c 對應的十進制)得到byte[] (0215)
仍是有一些字段翻譯過來不夠精細,詳細見官方文檔:www.bluetooth.com/specificati…