扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
全面的了解蓝牙协议栈架构:https://www.cnblogs.com/blogs-of-lxl/p/7010061.html
创新互联公司专注于本溪网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供本溪营销型网站建设,本溪网站制作、本溪网页设计、本溪网站官网定制、小程序开发服务,打造本溪网络公司原创品牌,更为您提供本溪网站排名全网营销落地服务。
蓝牙技术电子书: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
最近在做一个蓝牙开关的功能,发现一个很奇怪的现象:
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/lyh2299259684/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)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
继续找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
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网站上搜索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,找到下面这样一篇博客,说明了蓝牙协议栈通讯的来龙去脉。
############################################################################
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旋转找到一篇博客,最后有人提出一个解决办法 :
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找老外靠谱呢。
百度的一些修改蓝牙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)
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流