全面的瞭解藍牙協議棧架構:https://www.cnblogs.com/blogs-of-lxl/p/7010061.html html
藍牙技術電子書:https://www.crifan.com/files/doc/docbook/bluetooth_intro/release/html/bluetooth_intro.htmljava
藍牙4.0 BLE 廣播包解析:https://blog.csdn.net/qq576494799/article/details/52102642 android
HCI:https://blog.csdn.net/u010657219/article/details/42192481 api
HAL:http://www.javashuo.com/article/p-xzmpkbtl-mo.html 安全
https://blog.csdn.net/myarrow/article/details/7175204 架構
Android 做爲設備端app
開源庫:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2018/0403/9551.html less
demo : https://blog.csdn.net/z302766296/article/details/77816960 socket
https://www.jianshu.com/p/c4f84af432a1 ide
1.廣播包:http://www.javashuo.com/article/p-viwozoro-md.html
最近在作一個藍牙開關的功能,發現一個很奇怪的現象:
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類裏被調用
繼續搜索getRomoteService方法
1)在主設備BluetoothGattServer裏會被調用到
在其mBluetoothGattServerCallback裏會多處用到,用來建立一個BluetoothDevice對象,這個監聽是監遵從設備的。
注意:!!!!這些方法裏的address是從設備的地址,並非咱們想要的主設備的地址!!!
因此仍是得從從設備的代碼出發尋找mac是從哪裏獲取的。
從設備裏調用:
---------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------
在網站源碼搜索registerScanner:
registScanner方法
經過ScanManager的方法調用底層的ScanNative類註冊掃描器的方法:
最終找到JNI源碼調用的地方:/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
注意這裏的 CallVoidMethod 方法,調用的是java的方法:https://blog.csdn.net/lyh1299259684/article/details/79438802
onScanResult方法 (沒有找到這個方法在哪裏被調用了???)
其實吧,上面說了這麼多,仍是說從設備如何獲取mac的,並無說明主設備的mac究竟是什麼,怎麼暴露給從設備的呢?
後來才發現,客戶掃描的這個mac,其實是設備在開啓ble服務的時候經過廣播發出去的。設備本身定義的mac。而後前面這個推斷是錯誤的,這個mac地址仍是android系統本身生成的。
https://blog.csdn.net/shuijianbaozi/article/details/75219530 (關於廣播的mac爲何與本地的藍牙mac不一致的問題)
看看設備發廣播的源碼 (8.0源碼)
這裏的mBluetoothManager其實是BluetoothManagerService.java這個類
經過aidl和binder機制獲取IBluetoothGatt對象
這裏的binder對應的對象,咱們使用的是BLE,因此應該是GattService對象。
因此看看GattService的startAdvertisingSet方法,注意這些方法都會有兩個:
binder的方法
binder再會去調用GattService的方法
GattService再去調用AdvertiseManager的方法
注意上面,將advertiseData對象解析成了byte[]數據,也就是組織廣播包的數據。看看這個方法是如何解析的:
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 發起廣播的源碼解析: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)const btgatt_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
繼續找mac(Android 6.0源碼,由於當前咱們的系統就是6.0):
在上面源碼的閱讀過程當中,我發現有這樣的一個函數:
裏面有一個address,我猜測這應該就是我一直苦苦尋求的mac。那麼這個mac會回調到上層的某個回調裏嗎?事實證實前面的這個想法也是錯誤的,這裏的地址仍是客戶端本身的地址。
設備如何設置的mac:https://blog.csdn.net/shichaog/article/details/52100954
可是並無找到vendor_open方法調用的地方。
藍牙初始化
1)獲取藍牙地址
此次這個地址是隨機的,有多是我想要的mac。
enable
下面的這個博客,看完一張圖就能找到verdor_open的地方
https://blog.csdn.net/shichaog/article/details/52728684
根據上面的博客分析源碼:
藍牙Enable過程追蹤(Android 6.0源碼,由於當前咱們的系統就是6.0):
根據上面的博客分析源碼:
module_start_up:開啓了兩個module
一個module是hci_layer.c,在它的start_up方法裏調用了vendor->open方法。也就是前面我提到的博客裏的這個方法。
可是在vendor.c裏,發現這個local_bdaddr仍然是本機信息裏的那個固定的mac地址。
BTU_StartUp方法
》》》》BTU相關的源碼開始
SMP_Init()方法
L2CA_RegisterFixedChannel 這個方法貌似有mac的蹤跡
btm_ble_init()方法
這個地方貌似也有mac的蹤跡(這不正是開啓廣播start adv那個方法嗎?):
給address賦值,注意在賦值以後調用了 btm_ble_start_adv方法
BTE_InitStack()方法
》》》》BTU相關的源碼結束
2018.9.14日,繼續探索mac:
https://e2echina.ti.com/question_answer/wireless_connectivity/bluetooth/f/103/t/136174
http://www.javashuo.com/article/p-ronyydad-me.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網站上搜索rpa,找到了一些蛛絲馬跡:
/system/bt/stack/btm/btm_ble_multi_adv.c
/system/bt/stack/btm/btm_ble_addr.c
看這個類的註釋:
對ble地址的管理,哈哈,是否是有戲?
看了一下它的各個函數,基本與上面博客提到的幾種地址匹配上了。因爲咱們的設備是廣播一旦開啓,mac地址就會隨機的變化。因此我猜測這個類裏被調用的方法應該是Resolvable private address:
》》》》》》》》》》看btm_gen_resolve_paddr_cmpl方法:
這個方法, 和btsnd_hcic_ble_rand方法是關聯的,主要是判斷btsnd_hcic_ble_rand方法有沒有執行成功。因此主要看btsnd_hcic_ble_rand方法。
》》》》》》》》》》看btsnd_hcic_ble_rand方法
|
說實話,看到這兒,我只看到給變量p申請了個內存空間,pp與p創建的關聯,可是是哪裏賦值的呢?
看看這個方法:
|
|
|
|
到這裏,看得我一愣一愣的。 所幸我搜索android fixed_queue.c,找到下面這樣一篇博客,說明了藍牙協議棧通信的前因後果。
############################################################################
android bluedroid 協議棧裏面的各個組件之間的消息處理機制https://blog.csdn.net/yanli0084/article/details/51821064
############################################################################
只能向前追溯了:
經過搜索MSG_STACK_TO_HC_HCI_CMD找到對應的處理位置:
根據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); }
追蹤,
這不正是把數據發送給client_socket嗎?難道客戶端與設備端的藍牙通信,底層是走了socket通信?
那麼,設備端與客戶端的socket究竟是怎麼一回事,這個send方法又作了哪些事情。客戶端又是怎麼解析這些數據的呢?種種疑問立馬在我腦海裏浮現。
繼續追溯send的調用棧:
2018.09.17,繼續找mac:
/system/bt/stack/btm/btm_ble.c
上面的這些看似很像的方法,沒有被調用 。
繼續持之以恆的找mac
這個local_rpa能夠打印看一下,是不是mac。
|
追溯這個方法:
》》相關資料:
在HCI層ACL Connection的創建 https://blog.csdn.net/gjsisi/article/details/13021253
》》
|
|
|
|
這不是前面說到的btu與hci的通信嗎,經過任務隊列。
2018.9.18日,搜索mac旋轉找到一篇博客,最後有人提出一個解決辦法 :
將這個常量的true改爲false (此方法親測可用,就是不知道有什麼安全隱患。)
而這篇中文博客:https://blog.csdn.net/shuijianbaozi/article/details/75219530 ,只說了有地址旋轉這回事兒,可是沒有提出解決辦法。仍是google找老外靠譜呢。
百度的一些修改藍牙mac的方法,可是不適用於個人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做了區分的地方。
2)