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 
 2- cc_library_static { 
 name: "libbt-hci",
- 该static library会build进 - libbluetooth.so(- system/bt/main/Android.bp)- 1 
 2- cc_library_shared { 
 name: "libbluetooth",
- 另外,也会被 - libbt-stackstatic library(- system/bt/stack/Android.bp)所用。- 1 
 2- cc_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
 6- if (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
 7- if (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_thread1
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 socket- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- btsnoop_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
 4- do 
 {
 ret = snoop_process(sk);
 } while(ret != -1);- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- int 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
 18- switch (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 
 2- btsnoop_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。