1. 文件位置

适用范围: 本文主要针对Android Q的code

1. 文件位置

1.1 源文件

  1. Android原生:

    1
    system/bt/hci/src/btsnoop.cc
  2. qcm使用位置:

    1
    vendor/qcom/opensource/commonsys/system/bt/hci/src/btsnoop.cc

1.2 编译情况

  1. 源文件build出的static library为libbt-hci(system/bt/hci/Android.bp)

    1
    2
    cc_library_static {
    name: "libbt-hci",
  2. 该static library会build进libbluetooth.so(system/bt/main/Android.bp)

    1
    2
    cc_library_shared {
    name: "libbluetooth",
  3. 另外,也会被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. 模式种类

    1
    2
    3
    4
    5
    #define BTSNOOP_LOG_MODE_PROPERTY "persist.bluetooth.btsnooplogmode"
    #define BTSNOOP_DEFAULT_MODE_PROPERTY "persist.bluetooth.btsnoopdefaultmode"
    #define BTSNOOP_MODE_DISABLED "disabled"
    #define BTSNOOP_MODE_FILTERED "filtered"
    #define BTSNOOP_MODE_FULL "full"
  2. 默认模式
    在任何版本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. 高通模式

高通额外定义的模式有

1
2
#define BTSNOOP_MODE_SNOOPHEADERSFILTERED "snoopheadersfiltered"
#define BTSNOOP_MODE_MEDIAPKTSFILTERED "mediapktsfiltered"

  1. 默认模式
    在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.data(),
    BTSNOOP_MODE_FILTERED);

3. 对外提供接口与调用情况

用法大致介绍。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。在何处start modle,何处调用接口函数获得结构体变量,以及如何使用的

3.1 模块

3.1.1 模块名

1
static const char BTSNOOP_MODULE[] = "btsnoop_module";

3.1.2 模块的定义

1
2
3
4
5
6
7
EXPORT_SYMBOL extern const module_t btsnoop_module = {
.name = BTSNOOP_MODULE,
.init = NULL,
.start_up = start_up,
.shut_down = shut_down,
.clean_up = NULL,
.dependencies = {STACK_CONFIG_MODULE, NULL}};

3.2 接口函数

3.2.1 结构体

保存记录(过滤)packet函数指针的结构体

1
2
3
4
5
6
7
8
9
10
11
12
typedef 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
3
static 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为删非过滤的)等。

  1. 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
  1. 高通定义
    新增模式BTSNOOP_MODE_SNOOPHEADERSFILTEREDBTSNOOP_MODE_MEDIAPKTSFILTERED,新增变量is_vndbtsnoop_enabled(是否使用高通定义的snoop log记录方式)和vendor_logging_level

vendor_logging_level的取值如下

1
2
3
4
5
6
typedef 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。

  1. 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();
    }
  2. 高通则同时考虑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
4
case 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
13
void 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

  1. 根据情况删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
  1. 关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/.

  1. 高通添加,通过宏停止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
4
case 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完成),停止对应的thread

1
2
3
4
5
6
7
void stop_snoop_logging ()
{
...
snoop_thread_cleanup();
pthread_join(snoop_client_tid, NULL);
snoop_client_tid = -1;
}

snoop_thread_cleanup定义于`btsnoop_dump.c

1
2
3
4
5
6
7
8
void 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

  1. 根据不同的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;
    }
  2. 将数据写入local socket(高通会读取此socket中内容写入另一个socket)

    1
    2
    btsnoop_net_write(&header, sizeof(btsnoop_header_t));
    btsnoop_net_write(packet, length_he - 1);
  3. 同时写入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。