btsnoop_net.cc的分析
适用范围: 本文主要针对Android Q的code
1. 文件位置
1.1 源文件
Android原生:
1
system/bt/hci/src/btsnoop_net.cc
qcm使用位置:
1
vendor/qcom/opensource/commonsys/system/bt/hci/src/btsnoop_net.cc
1.2 官方使用文档
1 | system/bt/doc/btsnoop_net.md |
1.3 编译情况
源文件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. 主要功能
btsoop_net
通过本地TCP套接字公开蓝牙snoop日志,该套接字配合使用hcidump
可实时调试HCI数据。
使用的端口号如下(system/bt/doc/network_ports.md
):1
2TCP 8872 (hci/src/btsnoop_net.cc) - read live btsnoop logs
TCP 8873 (hci/src/hci_inject.cc) - inject HCI packets into HCI stream
备注:关于hci_inject.cc
的分析(待续。。。。。。。)
3. 使用方法
- 官方使用方法如下,详细可参见
btsnoop_net.md
在
“bt_stack.conf”
中设置“btsnooplogoutput=true”
启用。
重新启动stack,stack将侦听端口8872
上的传入TCP连接。
在Linux主机上对hcidump
使用此功能,运行:
1
2 $ adb forward tcp:8872 tcp:8872
$ nc localhost 8872 | hcidump -r /dev/stdin
- 本地测试校正后使用方法
经核实,源文件中并没有hcidump
。
。。。。。。。
4. 对外提供接口与调用情况
4.1 code中启用功能
需置BT_NET_DEBUG
为TRUE
,否则进入接口函数之后直接返回。
4.2 接口函数
1 | void btsnoop_net_open() |
4.2.1 btsnoop_net_open
创建thread执行listen_fn_
函数,主要功能由listen_fn_
实现(listen_fn_
的主要功能参见5)。1
2listen_thread_valid_ =
(pthread_create(&listen_thread_, NULL, listen_fn_, NULL) == 0);
4.2.2 btsnoop_net_write
如果存在client socket,就向其写入数据包1
send(client_socket_, data, length, 0)
4.2.3 btsnoop_net_close
停止thread,关socket
4.3 调用情况
这些接口函数仅会在btsnoop.cc
中被调用,调用的函数的对应情况如下
接口函数 | 调用函数 |
---|---|
btsnoop_net_open | start_up |
btsnoop_net_write | btsnoop_write_packet |
btsnoop_net_close | shut_down |
4.4 log
logcat log相关过滤关键字"bt_snoop_net"
。此file仅在出错时才会有log。
5. 起主要作用的listen\_fn\_
listen_fn_
开启端口号为8872
的localhost socket(127.0.0.7:8872
),作为server端侦听client端的请求。
5.1 创建socketlisten_socket_
1 | listen_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
5.2 创建local socket的地址结构
创建127.0.0.1:8872
的sockaddr_in
1
2
3
4struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(LOCALHOST_); //LOCALHOST_ = 0x7F000001, 127.0.0.1
addr.sin_port = htons(LISTEN_PORT_); //LISTEN_PORT_ = 8872
5.3 关联listen_socket_
和sockaddr_in
地址结构
1 | bind(listen_socket_, (struct sockaddr*)&addr, sizeof(addr) |
5.4 监听listen_socket_
1 | listen(listen_socket_, 10) |
5.5 受理连接请求
1 | client_socket = accept(listen_socket_, NULL, NULL) |
5.6 写入snoop log的文件头
1 | send(client_socket_, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16, 0) |
6. qcm相对原生code添加的功能
主要差异体现在listen\_fn\_
函数中
6.1 添加本地管道以终止thread
新建管道
1
2
3
4
5
6int self_pipe_fds[2];
pipe2(self_pipe_fds, O_NONBLOCK);
notification_listen_fd = self_pipe_fds[0];
notification_write_fd = self_pipe_fds[1];终止thread
用以终止跑
listen\_fn\_
函数的listen_thread_
。在
btsnoop_net_close
中调用notify_listen_thread()
。notify_listen_thread()
写notification_write_fd
pipe1
write(notification_write_fd, &buffer, 1)
listen\_fn\_
中侦听notification_listen_fd
的变化即返回1
2
3
4
5
6select(fd_max + 1, &sock_fds, NULL, NULL, NULL);
...
if((notification_listen_fd != -1) && FD_ISSET(notification_listen_fd, &sock_fds)) {
LOG_WARN(LOG_TAG, "%s exting from listen_fn_ thread ", __func__);
return NULL;
}
6.2 创建并使用高通的socket
1 | if (is_vndbtsnoop_enabled) { |
创建、绑定以及监听socket的工作主要由local_snoop_socket_create
函数实现,该函数定义于vendor\qcom\opensource\commonsys\bluetooth_ext\system_bt_ext\osi\src\vnd_log.cc
6.2.1 创建socket
1 | int listen_socket_local = socket(AF_LOCAL, SOCK_STREAM, 0); |
6.2.2 关联socket
1 | //LOCAL_SOCKET_NAME == "bthcitraffic" |
使用/tmp/bthcitraffic
(ANDROID_SOCKET_NAMESPACE_ABSTRACT
== /tmp/
)绑定socket。
6.2.3 监听socket
1 | listen(listen_socket_local, 1) |
6.2.4 受理client连接请求
回到btsnoop_net.cc
文件中(listen\_fn\_
函数),如果listen_socket_local
有变化,则受理client连接请求
1 | accept(listen_socket_local_, (struct sockaddr *)&cliaddr, (socklen_t *)&length); |
6.2.5 写入snoop log的文件头
1 | write(client_socket, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16) |
6.2.6 向btsnoop更新记录snoop log的socket文件描述符
调用update_snoop_fd(client_socket);
将client socket保存至logfile_fd
,替换录入log的文件描述符,并置sock_snoop_active
为true
。之后蓝牙数据包即会写入此client socket,不再写入原来设定的snoop log文件。
sock_snoop_active
为true
时,执行btsnoop.cc
中执行open_next_snoop_file
会直接返回。执行btsnoop_write_packet
不会因packet个数限制而打开下一个文件。
6.3 使用fd_set
管理多个文件描述符
6.3.1 将文件描述符加入集合
- 管道
1 | FD_SET(notification_listen_fd, &save_sock_fds); |
- local socket
listen_socket_
1 | FD_SET(listen_socket_, &save_sock_fds); |
- 高通的socket
1 | FD_SET(listen_socket_local_, &save_sock_fds); |
6.3.2 侦听集合内文件描述符的变化并处理
1 | for (;;) { |
7. qcm对listen_socket_local_
socket的使用
vendor\qcom\opensource\commonsys\bluetooth\bt_logger\src\btsnoop_dump.c
的snoop_connect_to_source
函数,该函数创建并连接server,并将该socket返回
7.1 创建socket
1 | btsnoop_socket = socket(AF_LOCAL, SOCK_STREAM, 0); |
7.2 设置socket地址结构
1 | memset(&serv_addr, 0, sizeof(serv_addr)); |
LOCAL_SOCKET_NAME
== "bthcitraffic"
7.3 关联socket和地址
1 | connect(btsnoop_socket, (struct sockaddr *)&serv_addr, addr_len); |
7.4 通过socket进行读写
在void *snoop_dump_thread()
函数中,先调用snoop_connect_to_source()
创建并连接socket的server端再通过该socket进行读取。1
2
3
4
5
6
7
8
9
10
11
12
13sk = snoop_connect_to_source();
//读取snoop log的文件头
bytes_recv = read_block (sk, &read_buf[0], 16);
//读取数据包
if (sk != -1)
{
do
{
ret = snoop_process(sk);
} while(ret != -1);
}