全面的了解蓝牙协议栈架构: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 (超级详细
蓝牙扫描
)
继续搜索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
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,找到下面这样一篇博客,说明了蓝牙协议栈通讯的来龙去脉。
############################################################################
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)