全面的了解藍牙協(xié)議棧架構:https://www.cnblogs.com/blogs-of-lxl/p/7010061.html
創(chuàng)新互聯(lián)公司專注于本溪網(wǎng)站建設服務及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供本溪營銷型網(wǎng)站建設,本溪網(wǎng)站制作、本溪網(wǎng)頁設計、本溪網(wǎng)站官網(wǎng)定制、小程序開發(fā)服務,打造本溪網(wǎng)絡公司原創(chuàng)品牌,更為您提供本溪網(wǎng)站排名全網(wǎng)營銷落地服務。
藍牙技術電子書:https://www.crifan.com/files/doc/docbook/bluetooth_intro/release/html/bluetooth_intro.html
藍牙4.0 BLE 廣播包解析:https://blog.csdn.net/qq576494799/article/details/52102642
HCI:https://blog.csdn.net/u010657219/article/details/42192481
HAL:https://blog.csdn.net/luoshengyang/article/details/6567257
https://blog.csdn.net/myarrow/article/details/7175204
Android 作為設備端
開源庫:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2018/0403/9551.html
demo : https://blog.csdn.net/z302766296/article/details/77816960
https://www.jianshu.com/p/c4f84af432a1
1.廣播包:https://www.cnblogs.com/CharlesGrant/p/7155211.html
最近在做一個藍牙開關的功能,發(fā)現(xiàn)一個很奇怪的現(xiàn)象:
1.打開藍牙,藍牙圖標亮了,但是藍牙不能被外界搜索到。只有從設置-藍牙進入藍牙掃描界面,此時藍牙才能被外界搜索到。所以準備一探源碼,看能否找到解決辦法。
藍牙enable源碼分析
https://blog.csdn.net/ccc905341846/article/details/79009200
https://blog.csdn.net/zrf1335348191/article/details/53215281
https://www.cnblogs.com/chenbin7/p/3334082.html (超級詳細
藍牙掃描
)
https://www.cnblogs.com/libs-liu/p/9166075.html (藍牙掃描)
從設備掃描主設備的藍牙,藍牙的地址,竟然不是主設備主機信息里顯示的藍牙地址,只有從源碼
分析這個地址了:
1.首先這個地址是BluetoothDevice這個類里的mAddress變量,這個變量是從BluetoothDevice的構造方法傳入的,而BluetoothDevice的構造方法在BluetoothAdapter類里被調(diào)用
繼續(xù)搜索getRomoteService方法
1)在主設備BluetoothGattServer里會被調(diào)用到
在其mBluetoothGattServerCallback里會多處用到,用來創(chuàng)建一個BluetoothDevice對象,這個監(jiān)聽是監(jiān)聽從設備的。
注意:?。。。∵@些方法里的address是從設備的地址,并不是我們想要的主設備的地址!??!
所以還是得從從設備的代碼出發(fā)尋找mac是從哪里獲取的。
從設備里調(diào)用:
---------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------
在網(wǎng)站源碼搜索registerScanner:
registScanner方法
通過ScanManager的方法調(diào)用底層的ScanNative類注冊掃描器的方法:
最終找到JNI源碼調(diào)用的地方:/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
注意這里的 CallVoidMethod 方法,調(diào)用的是java的方法:https://blog.csdn.net/lyh2299259684/article/details/79438802
onScanResult方法 (沒有找到這個方法在哪里被調(diào)用了???)
其實吧,上面說了這么多,還是說從設備如何獲取mac的,并沒有說明主設備的mac到底是什么,怎么暴露給從設備的呢?
后來才發(fā)現(xiàn),客戶掃描的這個mac,實際上是設備在開啟ble服務的時候通過廣播發(fā)出去的。設備自己定義的mac。然后前面這個推斷是錯誤的,這個mac地址還是android系統(tǒng)自己生成的。
https://blog.csdn.net/shuijianbaozi/article/details/75219530 (關于廣播的mac為什么與本地的藍牙m(xù)ac不一致的問題)
看看設備發(fā)廣播的源碼 (8.0源碼)
這里的mBluetoothManager實際上是BluetoothManagerService.java這個類
通過aidl和binder機制獲取IBluetoothGatt對象
這里的binder對應的對象,我們使用的是BLE,所以應該是GattService對象。
所以看看GattService的startAdvertisingSet方法,注意這些方法都會有兩個:
binder的方法
binder再會去調(diào)用GattService的方法
GattService再去調(diào)用AdvertiseManager的方法
注意上面,將advertiseData對象解析成了byte[]數(shù)據(jù),也就是組織廣播包的數(shù)據(jù)。看看這個方法是如何解析的:
package com.android.bluetooth.gatt; import android.bluetooth.BluetoothUuid; import android.bluetooth.le.AdvertiseData; import android.os.ParcelUuid; import android.util.Log; import com.android.bluetooth.Utils; import java.io.ByteArrayOutputStream; class AdvertiseHelper { private static final String TAG = "AdvertiseHelper"; private static final int DEVICE_NAME_MAX = ; private static final int COMPLETE_LIST__BIT_SERVICE_UUIDS = 0X; private static final int COMPLETE_LIST__BIT_SERVICE_UUIDS = 0X; private static final int COMPLETE_LIST__BIT_SERVICE_UUIDS = 0X; private static final int SHORTENED_LOCAL_NAME = 0X; private static final int COMPLETE_LOCAL_NAME = 0X; private static final int TX_POWER_LEVEL = 0x0A; private static final int SERVICE_DATA__BIT_UUID = 0X; private static final int SERVICE_DATA__BIT_UUID = 0X; private static final int SERVICE_DATA__BIT_UUID = 0X; private static final int MANUFACTURER_SPECIFIC_DATA = 0XFF; public static byte[] advertiseDataToBytes(AdvertiseData data, String name) { if (data == null) return new byte[0]; // Flags are added by lower layers of the stack, only if needed; // no need to add them here. ByteArrayOutputStream ret = new ByteArrayOutputStream(); if (data.getIncludeDeviceName()) { try { byte[] nameBytes = name.getBytes("UTF-8"); int nameLength = nameBytes.length; byte type; // TODO(jpawlowski) put a better limit on device name! if (nameLength > DEVICE_NAME_MAX) { nameLength = DEVICE_NAME_MAX; type = SHORTENED_LOCAL_NAME; } else { type = COMPLETE_LOCAL_NAME; } ret.write(nameLength + 1); ret.write(type); ret.write(nameBytes, 0, nameLength); } catch (java.io.UnsupportedEncodingException e) { Log.e(TAG, "Can't include name - encoding error!", e); } } for (int i = 0; i < data.getManufacturerSpecificData().size(); i++) { int manufacturerId = data.getManufacturerSpecificData().keyAt(i); byte[] manufacturerData = data.getManufacturerSpecificData().get(manufacturerId); int dataLen = 2 + (manufacturerData == null ? 0 : manufacturerData.length); byte[] concated = new byte[dataLen]; // First two bytes are manufacturer id in little-endian. concated[0] = (byte) (manufacturerId & 0xFF); concated[1] = (byte) ((manufacturerId >> 8) & 0xFF); if (manufacturerData != null) { System.arraycopy(manufacturerData, 0, concated, 2, manufacturerData.length); } ret.write(concated.length + 1); ret.write(MANUFACTURER_SPECIFIC_DATA); ret.write(concated, 0, concated.length); } if (data.getIncludeTxPowerLevel()) { ret.write(2 /* Length */); ret.write(TX_POWER_LEVEL); ret.write(0); // lower layers will fill this value. } if (data.getServiceUuids() != null) { ByteArrayOutputStream serviceUuids = new ByteArrayOutputStream(); ByteArrayOutputStream serviceUuids = new ByteArrayOutputStream(); ByteArrayOutputStream serviceUuids = new ByteArrayOutputStream(); for (ParcelUuid parcelUuid : data.getServiceUuids()) { byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid); if (uuid.length == BluetoothUuid.UUID_BYTES__BIT) { serviceUuids.write(uuid, 0, uuid.length); } else if (uuid.length == BluetoothUuid.UUID_BYTES__BIT) { serviceUuids.write(uuid, 0, uuid.length); } else /*if (uuid.length == BluetoothUuid.UUID_BYTES__BIT)*/ { serviceUuids.write(uuid, 0, uuid.length); } } if (serviceUuids.size() != 0) { ret.write(serviceUuids.size() + 1); ret.write(COMPLETE_LIST__BIT_SERVICE_UUIDS); ret.write(serviceUuids.toByteArray(), 0, serviceUuids.size()); } if (serviceUuids.size() != 0) { ret.write(serviceUuids.size() + 1); ret.write(COMPLETE_LIST__BIT_SERVICE_UUIDS); ret.write(serviceUuids.toByteArray(), 0, serviceUuids.size()); } if (serviceUuids.size() != 0) { ret.write(serviceUuids.size() + 1); ret.write(COMPLETE_LIST__BIT_SERVICE_UUIDS); ret.write(serviceUuids.toByteArray(), 0, serviceUuids.size()); } } if (!data.getServiceData().isEmpty()) { for (ParcelUuid parcelUuid : data.getServiceData().keySet()) { byte[] serviceData = data.getServiceData().get(parcelUuid); byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid); int uuidLen = uuid.length; int dataLen = uuidLen + (serviceData == null ? 0 : serviceData.length); byte[] concated = new byte[dataLen]; System.arraycopy(uuid, 0, concated, 0, uuidLen); if (serviceData != null) { System.arraycopy(serviceData, 0, concated, uuidLen, serviceData.length); } if (uuid.length == BluetoothUuid.UUID_BYTES__BIT) { ret.write(concated.length + 1); ret.write(SERVICE_DATA__BIT_UUID); ret.write(concated, 0, concated.length); } else if (uuid.length == BluetoothUuid.UUID_BYTES__BIT) { ret.write(concated.length + 1); ret.write(SERVICE_DATA__BIT_UUID); ret.write(concated, 0, concated.length); } else /*if (uuid.length == BluetoothUuid.UUID_BYTES__BIT)*/ { ret.write(concated.length + 1); ret.write(SERVICE_DATA__BIT_UUID); ret.write(concated, 0, concated.length); } } } return ret.toByteArray(); } }
Android 6.0 發(fā)起廣播的源碼解析:https://blog.csdn.net/lansefeiyang08/article/details/46545215
https://blog.csdn.net/lansefeiyang08/article/details/46505921
我對上面的博客源碼作一下補充:
1)結構體btgatt_interface_t的位置
2)btgatt_client_interface_t所在的位置 /hardware/libhardware/include/hardware/bt_gatt_client.h
3)constbtgatt_client_interface_t btgattClientInterface映射所在的目錄:/system/bt/btif/src/btif_gatt_client.c
4)BTA_GATTC_AppRegister方法所在目錄:/system/bt/bta/gatt/bta_gattc_api.c
注意C這一層的跳轉順序:bt_gatt_client.h 》btif_gatt_client.c 》bta_gattc_api.c
繼續(xù)找mac(Android 6.0源碼,因為當前我們的系統(tǒng)就是6.0):
在上面源碼的閱讀過程中,我發(fā)現(xiàn)有這樣的一個函數(shù):
里面有一個address,我猜想這應該就是我一直苦苦尋求的mac。那么這個mac會回調(diào)到上層的某個回調(diào)里嗎?事實證明前面的這個想法也是錯誤的,這里的地址還是客戶端自己的地址。
設備如何設置的mac:https://blog.csdn.net/shichaog/article/details/52100954
但是并沒有找到vendor_open方法調(diào)用的地方。
藍牙初始化
1)獲取藍牙地址
這次這個地址是隨機的,有可能是我想要的mac。
enable
下面的這個博客,看完一張圖就能找到verdor_open的地方
https://blog.csdn.net/shichaog/article/details/52728684
根據(jù)上面的博客分析源碼:
藍牙Enable過程追蹤(Android 6.0源碼,因為當前我們的系統(tǒng)就是6.0):
根據(jù)上面的博客分析源碼:
module_start_up:開啟了兩個module
一個module是hci_layer.c,在它的start_up方法里調(diào)用了vendor->open方法。也就是前面我提到的博客里的這個方法。
但是在vendor.c里,發(fā)現(xiàn)這個local_bdaddr仍然是本機信息里的那個固定的mac地址。
BTU_StartUp方法
》》》》BTU相關的源碼開始
SMP_Init()方法
L2CA_RegisterFixedChannel 這個方法貌似有mac的蹤影
btm_ble_init()方法
這個地方貌似也有mac的蹤影(這不正是開啟廣播start adv那個方法嗎?):
給address賦值,注意在賦值之后調(diào)用了 btm_ble_start_adv方法
BTE_InitStack()方法
》》》》BTU相關的源碼結束
2018.9.14日,繼續(xù)探索mac:
https://e2echina.ti.com/question_answer/wireless_connectivity/bluetooth/f/103/t/136174
https://www.cnblogs.com/CharlesGrant/p/7155812.html
https://blog.csdn.net/android_jiangjun/article/details/77113883
以上的博客,對ble廣播的mac種類都做了一些說明,但是沒有從源碼里指明這個mac是在哪里如何生成的。
看到有這樣的一個博客:https://devzone.nordicsemi.com/f/nordic-q-a/16720/setting-resolvable-private-address
通過在xref網(wǎng)站上搜索rpa,找到了一些蛛絲馬跡:
/system/bt/stack/btm/btm_ble_multi_adv.c
/system/bt/stack/btm/btm_ble_addr.c
看這個類的注釋:
對ble地址的管理,哈哈,是不是有戲?
看了一下它的各個函數(shù),基本與上面博客提到的幾種地址匹配上了。由于我們的設備是廣播一旦開啟,mac地址就會隨機的變化。所以我猜想這個類里被調(diào)用的方法應該是Resolvable private address:
》》》》》》》》》》看btm_gen_resolve_paddr_cmpl方法:
這個方法, 和btsnd_hcic_ble_rand方法是關聯(lián)的,主要是判斷btsnd_hcic_ble_rand方法有沒有執(zhí)行成功。所以主要看btsnd_hcic_ble_rand方法。
》》》》》》》》》》看btsnd_hcic_ble_rand方法
|
說實話,看到這兒,我只看到給變量p申請了個內(nèi)存空間,pp與p建立的關聯(lián),但是是哪里賦值的呢?
看看這個方法:
|
|
|
|
到這里,看得我一愣一愣的。 所幸我搜索android fixed_queue.c,找到下面這樣一篇博客,說明了藍牙協(xié)議棧通訊的來龍去脈。
############################################################################
https://blog.csdn.net/yanli0084/article/details/51821064
############################################################################
只能向前追溯了:
通過搜索MSG_STACK_TO_HC_HCI_CMD找到對應的處理位置:
根據(jù)case猜想到應該是btsnoop.c處理這個事件
看btsnoop_write_packet方法
static void btsnoop_write_packet(packet_type_t type, const uint8_t *packet, bool is_received) { int length_he = 0; int length; int flags; int drops = 0; switch (type) { case kCommandPacket: length_he = packet[2] + 4; flags = 2; break; case kAclPacket: length_he = (packet[3] << 8) + packet[2] + 5; flags = is_received; break; case kScoPacket: length_he = packet[2] + 4; flags = is_received; break; case kEventPacket: length_he = packet[1] + 3; flags = 3; break; } uint64_t timestamp = btsnoop_timestamp(); uint32_t time_hi = timestamp >> 32; uint32_t time_lo = timestamp & 0xFFFFFFFF; length = htonl(length_he); flags = htonl(flags); drops = htonl(drops); time_hi = htonl(time_hi); time_lo = htonl(time_lo); btsnoop_write(&length, 4); btsnoop_write(&length, 4); btsnoop_write(&flags, 4); btsnoop_write(&drops, 4); btsnoop_write(&time_hi, 4); btsnoop_write(&time_lo, 4); btsnoop_write(&type, 1); btsnoop_write(packet, length_he - 1); }
追蹤,
這不正是把數(shù)據(jù)發(fā)送給client_socket嗎?難道客戶端與設備端的藍牙通訊,底層是走了socket通訊?
那么,設備端與客戶端的socket到底是怎么一回事,這個send方法又做了哪些事情??蛻舳擞质窃趺唇馕鲞@些數(shù)據(jù)的呢?種種疑問立馬在我腦海里浮現(xiàn)。
繼續(xù)追溯send的調(diào)用棧:
2018.09.17,繼續(xù)找mac:
/system/bt/stack/btm/btm_ble.c
上面的這些看似很像的方法,沒有被調(diào)用 。
繼續(xù)鍥而不舍的找mac
這個local_rpa可以打印看一下,是否是mac。
|
追溯這個方法:
》》相關資料:
在HCI層ACL Connection的建立 https://blog.csdn.net/gjsisi/article/details/13021253
》》
|
|
|
|
這不是前面說到的btu與hci的通訊嗎,通過任務隊列。
2018.9.18日,搜索mac旋轉找到一篇博客,最后有人提出一個解決辦法 :
https://stackoverflow.com/questions/28602672/android-5-static-bluetooth-mac-address-for-ble-advertising
將這個常量的true改成false (此方法親測可用,就是不知道有什么安全隱患。)
而這篇中文博客:https://blog.csdn.net/shuijianbaozi/article/details/75219530 ,只說了有地址旋轉這回事兒,但是沒有提出解決辦法。還是google找老外靠譜呢。
百度的一些修改藍牙m(xù)ac的方法,但是不適用于我的6.0的設備。
http://bbs.gfan.com/android-4369727-1-1.html
https://jingyan.baidu.com/article/17bd8e5250b6be85ab2bb8bf.html (android hex editor修改不了文件)
全局搜索BLE_PRIVACY_SPT這個變量,找尋可能暴露mac的地方。
1)
這里就是對隨機還是固定的mac作了區(qū)分的地方。
2)