btsnoop.cc的分析
1. 文件位置
适用范围: 本文主要针对Android Q的code
1. 文件位置
1.1 源文件
Android原生:
1
system/bt/hci/src/btsnoop.cc
qcm使用位置:
1
vendor/qcom/opensource/commonsys/system/bt/hci/src/btsnoop.cc
1.2 编译情况
源文件build出的static library为
libbt-hci
(system/bt/hci/Android.bp
)1
2cc_library_static {
name: "libbt-hci",该static library会build进
libbluetooth.so
(system/bt/main/Android.bp
)1
2cc_library_shared {
name: "libbluetooth",另外,也会被
libbt-stack
static library(system/bt/stack/Android.bp
)所用。1
2cc_library_static {
name: "libbt-stack",
2. 主要功能
用于记录host和controller间的蓝牙数据包。另外,根据特定模式,可过滤相关数据包。
2.1 模式介绍
2.1.1 google原生
模式种类
1
2
3
4
5默认模式
在任何版本img下默认均为BTSNOOP_MODE_DISABLED
。1
2
3
4
5
6
7
8
9
10
11
12
13//#define IS_DEBUGGABLE_PROPERTY "ro.debuggable"
int is_debuggable = osi_property_get_int32(IS_DEBUGGABLE_PROPERTY, 0);
//#define BTSNOOP_MODE_DISABLED "disabled"
std::string default_mode = BTSNOOP_MODE_DISABLED;
if (is_debuggable) {
//#define BTSNOOP_DEFAULT_MODE_PROPERTY "persist.bluetooth.btsnoopdefaultmode"
int len = osi_property_get(BTSNOOP_DEFAULT_MODE_PROPERTY, property.data(),
BTSNOOP_MODE_DISABLED);
default_mode = std::string(property.data(), len);
}
2.1.2 高通定义
- 高通模式
高通额外定义的模式有1
2
- 默认模式
在debug版img下默认为BTSNOOP_MODE_FILTERED
,在其他情况下同google原生,为BTSNOOP_MODE_DISABLED
。1
2
3
4...
//#define BTSNOOP_MODE_FILTERED "filtered"
int len = osi_property_get(BTSNOOP_DEFAULT_MODE_PROPERTY, property (),
BTSNOOP_MODE_FILTERED);
3. 对外提供接口与调用情况
用法大致介绍。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。在何处start modle,何处调用接口函数获得结构体变量,以及如何使用的
3.1 模块
3.1.1 模块名
1 | static const char BTSNOOP_MODULE[] = "btsnoop_module"; |
3.1.2 模块的定义
1 | EXPORT_SYMBOL extern const module_t btsnoop_module = { |
3.2 接口函数
3.2.1 结构体
保存记录(过滤)packet函数指针的结构体1
2
3
4
5
6
7
8
9
10
11
12typedef struct btsnoop_t {
// |is_received| = true, incoming packet. Otherwise, outgoing.
void (*capture)(const BT_HDR* packet, bool is_received);
void (*whitelist_l2c_channel)(uint16_t conn_handle, uint16_t local_cid,
uint16_t remote_cid);
void (*whitelist_rfc_dlci)(uint16_t local_cid, uint8_t dlci);
void (*add_rfc_l2c_channel)(uint16_t conn_handle, uint16_t local_cid,
uint16_t remote_cid);
void (*clear_l2cap_whitelist)(uint16_t conn_handle, uint16_t local_cid,
uint16_t remote_cid);
} btsnoop_t;
3.2.2 结构体变量
结构体变量interface
的赋值1
2
3static const btsnoop_t interface = {capture, whitelist_l2c_channel,
whitelist_rfc_dlci, add_rfc_l2c_channel,
clear_l2cap_whitelist};
3.2.3 获取结构体变量
获取上述结构体变量的接口函数1
const btsnoop_t* btsnoop_get_interface() { return &interface; }
3.3 主要函数分析
3.3.1 start_up
3.3.1.1 设定模式
设定模式(模式及其设定的情况可参见2)。
3.3.1.2 设定变量
根据设置的模式设定用于判定记录数据包用的变量值如is_btsnoop_enabled
(是否使用原生的snoop log记录方式),is_btsnoop_filtered
(是否过滤数据包),调用delete_btsnoop_files
函数删除非过滤的snoop log file还是过滤的file(true为删过滤的file,false为删非过滤的)等。
- google 原生
模式 | is_btsnoop_enabled | is_btsnoop_filtered | delete_btsnoop_files |
---|---|---|---|
BTSNOOP_MODE_FILTERED | true | true | false |
BTSNOOP_MODE_FULL | true | false | true |
其他,即BTSNOOP_MODE_DISABLED | false | false | true+false |
- 高通定义
新增模式BTSNOOP_MODE_SNOOPHEADERSFILTERED
和BTSNOOP_MODE_MEDIAPKTSFILTERED
,新增变量is_vndbtsnoop_enabled
(是否使用高通定义的snoop log记录方式)和vendor_logging_level
。
vendor_logging_level
的取值如下1
2
3
4
5
6typedef enum {
HCI_SNOOP_LOG_FULL = 1, // Complete HCI snoop logs with media packets
DYNAMIC_LOGCAT_CAPTURE = 2, // Level 6 logcat logs over logger socket
HCI_SNOOP_LOG_LITE = 4, // Always enabled, HCI snoop logs sans media packets
HCI_SNOOP_ONLY_HEADER = 8, // HCI snoop logs with only ACL Header packets
} LoggingFlags;
模式 | is_btsnoop_enabled | is_btsnoop_filtered | delete_btsnoop_files | is_vndbtsnoop_enabled | vendor_logging_level |
---|---|---|---|---|---|
BTSNOOP_MODE_FILTERED | true | true | false | false | – |
BTSNOOP_MODE_FULL | false | false | true | true | 3 |
BTSNOOP_MODE_MEDIAPKTSFILTERED | false | false | – | true | 6 |
BTSNOOP_MODE_SNOOPHEADERSFILTERED | false | false | – | true | 10 |
其他,即BTSNOOP_MODE_DISABLED | false | false | true+false | false | – |
根据is_vndbtsnoop_enabled
的值设置”persist.bluetooth.btsnoopenable”(底层根据该prop判断是否记录snoop log)
3.3.1.3 开始录snoop log
根据变量的赋值情况判断是否开始录制snoop log。
google原生仅根据
is_btsnoop_enabled
的情况判断,snoop log file的大小通过读取BTSNOOP_MAX_PACKETS_PROPERTY
("persist.bluetooth.btsnoopsize"
) prop决定1
2
3
4
5
6if (is_btsnoop_enabled) {
open_next_snoop_file();
packets_per_file = osi_property_get_int32(BTSNOOP_MAX_PACKETS_PROPERTY,
DEFAULT_BTSNOOP_SIZE);
btsnoop_net_open();
}高通则同时考虑
is_vndbtsnoop_enabled
,并使用默认的snoop log大小1
2
3
4
5
6
7if (is_btsnoop_enabled || is_vndbtsnoop_enabled) {
open_next_snoop_file();
packets_per_file = (//osi_property_get_int32(BTSNOOP_MAX_PACKETS_PROPERTY,
DEFAULT_BTSNOOP_SIZE);
btsnoop_net_open();
START_SNOOP_LOGGING();
}btsnoop_net_open
打开local socket,通过本地TCP套接字公开蓝牙snoop日志。关于此部分的详细分析可参见https://junswg.github.io/btsnoop-net-cc%E7%9A%84%E5%88%86%E6%9E%90/.详解
START_SNOOP_LOGGING
1
2
3#define GENERATE_VND_LOGS() if(logger_interface)logger_interface->send_event(GENERATE_VND_LOG_SIGNAL)
#define START_SNOOP_LOGGING() if(logger_interface)logger_interface->send_event(START_SNOOP_SIGNAL)
#define STOP_SNOOP_LOGGING() if(logger_interface)logger_interface->send_event(STOP_SNOOP_SIGNAL)
在bt_logger.c (vendor\qcom\opensource\commonsys\bluetooth\bt_logger\src)
的int process_packet(bt_log_buffer_t *log_list, char *l_data, short int pkt_len)
函数中会对该宏做处理1
2
3
4case START_SNOOP_SIGNAL:
start_snoop_logging();
ret = 1;
break;
start_snoop_logging
函数定义于btsnoop_dump.c (vendor\qcom\opensource\commonsys\bluetooth\bt_logger\src)
,主要创建thread跑函数snoop_dump_thread
1
2
3
4
5
6
7
8
9
10
11
12
13void start_snoop_logging ()
{
bool snoop_thread_valid = false;
snoop_log("starting snoop logging");
snoop_thread_valid = (pthread_create(&snoop_client_tid, NULL, snoop_dump_thread, NULL) == 0);
if (!snoop_thread_valid) {
snoop_log("pthread_create failed: %s", strerror(errno));
} else {
snoop_log(" snoop_dump_thread is initialized");
}
}
函数snoop_dump_thread
的主要功能,开client socket侦听"bthcitraffic"
,接收数据并写入snoop log文件
- 设定snoop log大小(20M-150M)
调用
snoop_connect_to_source
函数创建client socket1
2
3
4
5
6
7
8
9
10btsnoop_socket = socket(AF_LOCAL, SOCK_STREAM, 0);
...
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sun_family = AF_LOCAL;
//LOCAL_SOCKET_NAME "bthcitraffic"
strlcpy(&serv_addr.sun_path[1], LOCAL_SOCKET_NAME, strlen(LOCAL_SOCKET_NAME) + 1);
addr_len = strlen(LOCAL_SOCKET_NAME) + 1;
addr_len += sizeof(serv_addr.sun_family);
...
ret = connect(btsnoop_socket, (struct sockaddr *)&serv_addr, addr_len);读取snoop log
读取文件头
1
bytes_recv = read_block (sk, &read_buf[0], 16);
通过反复调用
snoop_process
来读取数据包,并写入snoop log的文件1
2
3
4do
{
ret = snoop_process(sk);
} while(ret != -1);1
2
3
4
5
6
7
8
9
10
11
12int snoop_process (int sk)
{
...
snoop_open_file();
...
read_block (sk, &read_buf[0], 8);
length = read_buf[0] << 24 | read_buf[1] << 16 | read_buf[2] << 8 | read_buf[3];
...
bytes_recv = read_block (sk, &read_buf[8], length + 16);
...
write(file_descriptor, read_buf, bytes_recv + 8);
}
1
2
3
4
5
6
7
8
int read_block (int sock, unsigned char *pBuf, int len)
{
do
{
recv(sock, &pBuf[bytes_recv], len - bytes_recv, 0);
bytes_recv += ret;
}while(bytes_recv < len);
}
3.3.2 shut_down
- 根据情况删snoop log
is_btsnoop_enabled | is_btsnoop_filtered | snoop log |
---|---|---|
true | true | 删snoop log |
true | false | 删filter snoop log |
false | – | 删snoop log和filter snoop log |
- 关local socket,
1
if (is_btsnoop_enabled) btsnoop_net_close();
关于此部分的详细分析可参见https://junswg.github.io/btsnoop-net-cc%E7%9A%84%E5%88%86%E6%9E%90/.
高通添加,通过宏停止snoop log的写入
1
if(is_vndbtsnoop_enabled) STOP_SNOOP_LOGGING();
- 详解
START_SNOOP_LOGGING
定义可参见
bt_logger_lib.h(vendor\qcom\opensource\commonsys\system\bt\include)
1
#define STOP_SNOOP_LOGGING() if(logger_interface)logger_interface->send_event(STOP_SNOOP_SIGNAL)
- 详解
在bt_logger.c (vendor\qcom\opensource\commonsys\bluetooth\bt_logger\src)
的int process_packet(bt_log_buffer_t *log_list, char *l_data, short int pkt_len)
函数中会对该宏做处理1
2
3
4case STOP_SNOOP_SIGNAL:
stop_snoop_logging();
ret = 1;
break;
stop_snoop_logging
函数定义于btsnoop_dump.c (vendor\qcom\opensource\commonsys\bluetooth\bt_logger\src)
,主要关snoop log file,关闭侦听数据包的client socket(前两者由snoop_thread_cleanup
完成),停止对应的thread1
2
3
4
5
6
7void stop_snoop_logging ()
{
...
snoop_thread_cleanup();
pthread_join(snoop_client_tid, NULL);
snoop_client_tid = -1;
}
snoop_thread_cleanup
定义于`btsnoop_dump.c1
2
3
4
5
6
7
8void snoop_thread_cleanup()
{
close_fd(&file_descriptor);
if (btsnoop_socket != -1) {
shutdown(btsnoop_socket, SHUT_RDWR);
close_fd(&btsnoop_socket);
}
}
3.3.3 btsnoop_write_packet
根据不同的packet type读取存放packet len的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18switch (type) {
case kCommandPacket:
length_he = packet[2] + 4;
flags = 2;
break;
case kAclPacket:
calculate_acl_packet_length(&length_he, packet, is_received);
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;
}将数据写入local socket(高通会读取此socket中内容写入另一个socket)
1
2btsnoop_net_write(&header, sizeof(btsnoop_header_t));
btsnoop_net_write(packet, length_he - 1);同时写入snoop log
1
TEMP_FAILURE_RETRY(writev(logfile_fd, iov, 2));
3.3.4 capture
3.3.5 update_snoop_fd
3.4 log
logcat log相关过滤关键字”bt_snoop”。此file仅在出错时才会有log。