• Android 7 btsnoop代码介绍


    本文假设你有btsnoop的概念,会在以上基础上进行android 7的btsnoop的代码介绍,如果你没有btsnoop相关的基础,那么移步到大专兰看btsnoop的概念,再来看本文,协议栈大专栏以及btsnoop的相关的文章连接如下:

    一篇文章足够你学习蓝牙技术,提供史上最全的蓝牙技术(传统蓝牙/低功耗蓝牙)文章总结,文档下载总结(2020/12/11更新)_Wireless_Link的博客-CSDN博客_蓝牙eir

    蓝牙协议栈学习/开发利器-BTSNOOP介绍_Wireless_Link的博客-CSDN博客_btsnoop

    本文通过以下几个内容来介绍下Android 7的btsnoop

    1)btsnoop的分类

    2)btsnoop的启动以及结束实现

    3)btsnoop的写入实现以及调用

    一.Btsnoop的分类

    我们可以看到Android7的AOSP代码中有3中btsnoop的实现,分别是btsnoop,btsnoop_net,btsnoop_mem,下面我们就要分别介绍下:

    1.btsnoop

    此方式就是普通的btsnoop,把hci的数据写入到文件中,然后导出文件来查看,文件源码跟头文件分别是:

    system/bt/hci/src/btsnoop.c

    system/bt/hci/include/btsnoop.h

    2.btsnoop_net

    此方式是通过socket来调试btsnoop,这种方式会把hci数据写入到local host的tcp 8872端口上,然后配合自己敲的指令,来实时抓取log,文件源码是:

    system/bt/hci/src/btsnoop_net.c

    这个功能的使用文档在

    system/bt/doc

    3.btsnoop_mem

    此方式是通过把btsnoop的数据抓下来,在btif层保存到一个ring buffer中,然后通过dump的方式在通过dprintf打印出来!,文件源码跟头文件分别是:

    system/bt/hci/src/btsnoop_mem.c

    system/bt/hci/include/btsnoop_mem.h

    二.btsnoop的启动实现

    btsnoop的启动分为以下几个步骤:

    1)模块启动

    2)模块启动的源码分析

    下面我们就一一分析下以上几个步骤

    1.模块启动

    Android的协议栈把很多功能都分成了一个个的子模块,叫做module,通过module_init来初始化,通过module_start_up来开始,通过module_shut_down来结束,module的实现不在本文章的讨论范围内,我们只需要知道模块提前根据以下结构体注册,然后每个函数指针调用到特定的函数即可!

    1. typedef struct {
    2. const char *name;
    3. module_lifecycle_fn init;
    4. module_lifecycle_fn start_up;
    5. module_lifecycle_fn shut_down;
    6. module_lifecycle_fn clean_up;
    7. const char *dependencies[];
    8. } module_t;

    btsnoop的module结构体如下:

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

    因为btsnoop没有init函数,只有startup跟shurdown函数,分别调用位置如下:

    1. void bte_main_enable()
    2. {
    3. APPL_TRACE_DEBUG("%s", __FUNCTION__);
    4. module_start_up(get_module(BTSNOOP_MODULE)); //模块启动
    5. module_start_up(get_module(HCI_MODULE));
    6. BTU_StartUp();
    7. }
    1. void bte_main_disable(void)
    2. {
    3. APPL_TRACE_DEBUG("%s", __FUNCTION__);
    4. module_shut_down(get_module(HCI_MODULE)); //模块结束
    5. module_shut_down(get_module(BTSNOOP_MODULE));
    6. BTU_ShutDown();
    7. }

    2. btsnoop的启动以及结束实现

    2.1 btsnoop的开启

    btsnoop的startup函数实现如下:

    1. static future_t *start_up(void) {
    2. module_started = true;
    3. update_logging();
    4. return NULL;
    5. }
    1. static void update_logging() {
    2. bool should_log = module_started &&
    3. (logging_enabled_via_api || stack_config->get_btsnoop_turned_on());
    4. if (should_log == is_logging)
    5. return;
    6. is_logging = should_log;
    7. if (should_log) {
    8. btsnoop_net_open();
    9. const char *log_path = stack_config->get_btsnoop_log_path();
    10. // Save the old log if configured to do so
    11. if (stack_config->get_btsnoop_should_save_last()) {
    12. char last_log_path[PATH_MAX];
    13. snprintf(last_log_path, PATH_MAX, "%s.%" PRIu64, log_path,
    14. btsnoop_timestamp());
    15. if (!rename(log_path, last_log_path) && errno != ENOENT)
    16. LOG_ERROR(LOG_TAG, "%s unable to rename '%s' to '%s': %s", __func__, log_path, last_log_path, strerror(errno));
    17. }
    18. logfile_fd = open(log_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
    19. if (logfile_fd == INVALID_FD) {
    20. LOG_ERROR(LOG_TAG, "%s unable to open '%s': %s", __func__, log_path, strerror(errno));
    21. is_logging = false;
    22. return;
    23. }
    24. write(logfile_fd, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16);
    25. } else {
    26. if (logfile_fd != INVALID_FD)
    27. close(logfile_fd);
    28. logfile_fd = INVALID_FD;
    29. btsnoop_net_close();
    30. }
    31. }

    startup主要是update_logging函数的实现,我们来分析一下

    我们看到是否开启要依赖于should_log这个变量,这个变量为true就做一些动作,比如btsnoop写文件的open,btsnoop_net的开启,如果为false就做btsoop写文件的关闭以及btsnoop_net的关闭。

    那么shoud_log都会依赖于什么呢?他的条件是这样的module_started && (logging_enabled_via_api || stack_config->get_btsnoop_turned_on());

    首先会依赖于module_started ,我们可以看到btsnoop startup的时候就把这个设置为true了,那么其他两个条件是从什么地方而来呢?

    logging_enabled_via_api

    config_hci_snoop_log-》btsnoop_get_interface()->set_api_wants_to_log(enable)-》logging_enabled_via_api = value,可以看出来config_hci_snoop_log这个bluetooth.c中的hal实现,所以这个是跟上层jni调用,由上层来决定

    那么stack_config->get_btsnoop_turned_on()这个条件呢?其中函数实现是get_btsnoop_turned_on

    1. static bool get_btsnoop_turned_on(void) {
    2. return config_get_bool(config, CONFIG_DEFAULT_SECTION, BTSNOOP_TURNED_ON_KEY, false);
    3. }

    所以看代码我们可以得出他是从"/etc/bluetooth/bt_stack.conf" 配置文件中读取BtSnoopLogOutput的key value来决定。

    基于以上条件,我们就能进入正式的开启文件等动作了,我们通过代码注释来解析

    1. if (should_log) {
    2. //btsnoop net的open,这个稍后分析
    3. btsnoop_net_open();
    4. // 通过/etc/bluetooth/bt_stack.conf配置文件的BtSnoopFileName value值来决定btsnoop的路径
    5. // 默认路径是/data/misc/bluedroid/btsnoop_hci.log
    6. const char *log_path = stack_config->get_btsnoop_log_path();
    7. // 通过/etc/bluetooth/bt_stack.conf配置文件的BtSnoopSaveLog value值来决定是否保存上一次的
    8. // btsnoop,这个功能主要是会把上一次的snoop修改名称,做一个备份
    9. // Save the old log if configured to do so
    10. if (stack_config->get_btsnoop_should_save_last()) {
    11. char last_log_path[PATH_MAX];
    12. snprintf(last_log_path, PATH_MAX, "%s.%" PRIu64, log_path,
    13. btsnoop_timestamp());
    14. if (!rename(log_path, last_log_path) && errno != ENOENT)
    15. LOG_ERROR(LOG_TAG, "%s unable to rename '%s' to '%s': %s", __func__, log_path, last_log_path, strerror(errno));
    16. }
    17. // 常规的打开文件,保存路径就是我们上面从解析文件中得到的
    18. logfile_fd = open(log_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
    19. if (logfile_fd == INVALID_FD) {
    20. LOG_ERROR(LOG_TAG, "%s unable to open '%s': %s", __func__, log_path, strerror(errno));
    21. is_logging = false;
    22. return;
    23. }
    24. // 写btsnoop的file header format,里面值不懂的可以回头看看我们的btsnoop的概念
    25. write(logfile_fd, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16);
    26. }

    现在回头来看看btsnoop_net的open

    1. // 如果定义的宏,并且为TRUE,就创建一个现成,执行listen_fn_函数
    2. void btsnoop_net_open() {
    3. #if (!defined(BT_NET_DEBUG) || (BT_NET_DEBUG != TRUE))
    4. return; // Disable using network sockets for security reasons
    5. #endif
    6. listen_thread_valid_ = (pthread_create(&listen_thread_, NULL, listen_fn_, NULL) == 0);
    7. if (!listen_thread_valid_) {
    8. LOG_ERROR(LOG_TAG, "%s pthread_create failed: %s", __func__, strerror(errno));
    9. } else {
    10. LOG_DEBUG(LOG_TAG, "initialized");
    11. }
    12. }
    13. static void *listen_fn_(UNUSED_ATTR void *context) {
    14. prctl(PR_SET_NAME, (unsigned long)LISTEN_THREAD_NAME_, 0, 0, 0);
    15. // 创建一个TCP的socket
    16. listen_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    17. if (listen_socket_ == -1) {
    18. LOG_ERROR(LOG_TAG, "%s socket creation failed: %s", __func__, strerror(errno));
    19. goto cleanup;
    20. }
    21. int enable = 1;
    22. if (setsockopt(listen_socket_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == -1) {
    23. LOG_ERROR(LOG_TAG, "%s unable to set SO_REUSEADDR: %s", __func__, strerror(errno));
    24. goto cleanup;
    25. }
    26. // 设置IP地址为local host,也就是本地交互,设置端口号为8872
    27. struct sockaddr_in addr;
    28. addr.sin_family = AF_INET;
    29. addr.sin_addr.s_addr = htonl(LOCALHOST_);
    30. addr.sin_port = htons(LISTEN_PORT_);
    31. if (bind(listen_socket_, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
    32. LOG_ERROR(LOG_TAG, "%s unable to bind listen socket: %s", __func__, strerror(errno));
    33. goto cleanup;
    34. }
    35. // 启动socket监听
    36. if (listen(listen_socket_, 10) == -1) {
    37. LOG_ERROR(LOG_TAG, "%s unable to listen: %s", __func__, strerror(errno));
    38. goto cleanup;
    39. }
    40. // 有设备接入 ,发送btsnoop的header file format过去
    41. for (;;) {
    42. int client_socket;
    43. OSI_NO_INTR(client_socket = accept(listen_socket_, NULL, NULL));
    44. if (client_socket == -1) {
    45. if (errno == EINVAL || errno == EBADF) {
    46. break;
    47. }
    48. LOG_WARN(LOG_TAG, "%s error accepting socket: %s", __func__, strerror(errno));
    49. continue;
    50. }
    51. /* When a new client connects, we have to send the btsnoop file header. This allows
    52. a decoder to treat the session as a new, valid btsnoop file. */
    53. pthread_mutex_lock(&client_socket_lock_);
    54. safe_close_(&client_socket_);
    55. client_socket_ = client_socket;
    56. OSI_NO_INTR(send(client_socket_, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16, 0));
    57. pthread_mutex_unlock(&client_socket_lock_);
    58. }
    59. cleanup:
    60. safe_close_(&listen_socket_);
    61. return NULL;
    62. }

    2.2 btsnoop的关闭

    1. {
    2. if (logfile_fd != INVALID_FD)
    3. close(logfile_fd); //关闭掉btsnoop的文件描述符
    4. logfile_fd = INVALID_FD;
    5. btsnoop_net_close(); // 关闭掉btsnoop_net
    6. }
    7. void btsnoop_net_close() {
    8. #if (!defined(BT_NET_DEBUG) || (BT_NET_DEBUG != TRUE))
    9. return; // Disable using network sockets for security reasons
    10. #endif
    11. if (listen_thread_valid_) {
    12. shutdown(listen_socket_, SHUT_RDWR);
    13. pthread_join(listen_thread_, NULL);
    14. safe_close_(&client_socket_);
    15. listen_thread_valid_ = false;
    16. }
    17. }

    三.btsnoop的写入实现以及调用

    1.btsnoop的写入实现

    首先他是通过capture接口来向外开放

    1. static void capture(const BT_HDR *buffer, bool is_received) {
    2. const uint8_t *p = buffer->data + buffer->offset;
    3. btsnoop_mem_capture(buffer);
    4. if (logfile_fd == INVALID_FD)
    5. return;
    6. switch (buffer->event & MSG_EVT_MASK) {
    7. case MSG_HC_TO_STACK_HCI_EVT:
    8. btsnoop_write_packet(kEventPacket, p, false);
    9. break;
    10. case MSG_HC_TO_STACK_HCI_ACL:
    11. case MSG_STACK_TO_HC_HCI_ACL:
    12. btsnoop_write_packet(kAclPacket, p, is_received);
    13. break;
    14. case MSG_HC_TO_STACK_HCI_SCO:
    15. case MSG_STACK_TO_HC_HCI_SCO:
    16. btsnoop_write_packet(kScoPacket, p, is_received);
    17. break;
    18. case MSG_STACK_TO_HC_HCI_CMD:
    19. btsnoop_write_packet(kCommandPacket, p, true);
    20. break;
    21. }
    22. }
    1. static void btsnoop_write_packet(packet_type_t type, const uint8_t *packet, bool is_received) {
    2. int length_he = 0;
    3. int length;
    4. int flags;
    5. int drops = 0;
    6. switch (type) {
    7. case kCommandPacket:
    8. length_he = packet[2] + 4;
    9. flags = 2;
    10. break;
    11. case kAclPacket:
    12. length_he = (packet[3] << 8) + packet[2] + 5;
    13. flags = is_received;
    14. break;
    15. case kScoPacket:
    16. length_he = packet[2] + 4;
    17. flags = is_received;
    18. break;
    19. case kEventPacket:
    20. length_he = packet[1] + 3;
    21. flags = 3;
    22. break;
    23. }
    24. uint64_t timestamp = btsnoop_timestamp();
    25. uint32_t time_hi = timestamp >> 32;
    26. uint32_t time_lo = timestamp & 0xFFFFFFFF;
    27. length = htonl(length_he);
    28. flags = htonl(flags);
    29. drops = htonl(drops);
    30. time_hi = htonl(time_hi);
    31. time_lo = htonl(time_lo);
    32. btsnoop_write(&length, 4);
    33. btsnoop_write(&length, 4);
    34. btsnoop_write(&flags, 4);
    35. btsnoop_write(&drops, 4);
    36. btsnoop_write(&time_hi, 4);
    37. btsnoop_write(&time_lo, 4);
    38. btsnoop_write(&type, 1);
    39. btsnoop_write(packet, length_he - 1);
    40. }
    41. static void btsnoop_write(const void *data, size_t length) {
    42. if (logfile_fd != INVALID_FD)
    43. write(logfile_fd, data, length);
    44. btsnoop_net_write(data, length);
    45. }
    46. void btsnoop_net_write(const void *data, size_t length) {
    47. #if (!defined(BT_NET_DEBUG) || (BT_NET_DEBUG != TRUE))
    48. return; // Disable using network sockets for security reasons
    49. #endif
    50. pthread_mutex_lock(&client_socket_lock_);
    51. if (client_socket_ != -1) {
    52. ssize_t ret;
    53. OSI_NO_INTR(ret = send(client_socket_, data, length, 0));
    54. if (ret == -1 && errno == ECONNRESET) {
    55. safe_close_(&client_socket_);
    56. }
    57. }
    58. pthread_mutex_unlock(&client_socket_lock_);
    59. }

    以上代码我觉得除了格式之外没有什么可讲的,如果你对为什么这么写格式不了解,我还是建议你回头看看。btsnoop的概念,下面我们来说明下调用地方

    2.btsnoop的写入调用

    通过以下函数实现来获取到btsnoop的interface函数操作

    1. const hci_t *hci_layer_get_interface() {
    2. buffer_allocator = buffer_allocator_get_interface();
    3. hal = hci_hal_get_interface();
    4. btsnoop = btsnoop_get_interface(); // 通过这个interface来实现
    5. hci_inject = hci_inject_get_interface();
    6. packet_fragmenter = packet_fragmenter_get_interface();
    7. vendor = vendor_get_interface();
    8. low_power_manager = low_power_manager_get_interface();
    9. init_layer_interface();
    10. return &interface;
    11. }

    host->controller方向的写入实现,通过这个调用btsnoop->capture(packet, false);

    1. static void transmit_fragment(BT_HDR *packet, bool send_transmit_finished) {
    2. uint16_t event = packet->event & MSG_EVT_MASK;
    3. serial_data_type_t type = event_to_data_type(event);
    4. btsnoop->capture(packet, false);
    5. hal->transmit_data(type, packet->data + packet->offset, packet->len);
    6. if (event != MSG_STACK_TO_HC_HCI_CMD && send_transmit_finished)
    7. buffer_allocator->free(packet);
    8. }

    controller->host方向的写入实现,这个函数比较复杂,你们暂时只需要知道调用了btsnoop->capture(packet, false)写入到btsnoop即可,因为其他实现暂时不在我们本文章套路范围之内

    1. static void hal_says_data_ready(serial_data_type_t type) {
    2. packet_receive_data_t *incoming = &incoming_packets[PACKET_TYPE_TO_INBOUND_INDEX(type)];
    3. uint8_t byte;
    4. while (hal->read_data(type, &byte, 1) != 0) {
    5. switch (incoming->state) {
    6. case BRAND_NEW:
    7. // Initialize and prepare to jump to the preamble reading state
    8. incoming->bytes_remaining = preamble_sizes[PACKET_TYPE_TO_INDEX(type)];
    9. memset(incoming->preamble, 0, PREAMBLE_BUFFER_SIZE);
    10. incoming->index = 0;
    11. incoming->state = PREAMBLE;
    12. // INTENTIONAL FALLTHROUGH
    13. case PREAMBLE:
    14. incoming->preamble[incoming->index] = byte;
    15. incoming->index++;
    16. incoming->bytes_remaining--;
    17. if (incoming->bytes_remaining == 0) {
    18. // For event and sco preambles, the last byte we read is the length
    19. incoming->bytes_remaining = (type == DATA_TYPE_ACL) ? RETRIEVE_ACL_LENGTH(incoming->preamble) : byte;
    20. size_t buffer_size = BT_HDR_SIZE + incoming->index + incoming->bytes_remaining;
    21. incoming->buffer = (BT_HDR *)buffer_allocator->alloc(buffer_size);
    22. if (!incoming->buffer) {
    23. LOG_ERROR(LOG_TAG, "%s error getting buffer for incoming packet of type %d and size %zd", __func__, type, buffer_size);
    24. // Can't read any more of this current packet, so jump out
    25. incoming->state = incoming->bytes_remaining == 0 ? BRAND_NEW : IGNORE;
    26. break;
    27. }
    28. // Initialize the buffer
    29. incoming->buffer->offset = 0;
    30. incoming->buffer->layer_specific = 0;
    31. incoming->buffer->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)];
    32. memcpy(incoming->buffer->data, incoming->preamble, incoming->index);
    33. incoming->state = incoming->bytes_remaining > 0 ? BODY : FINISHED;
    34. }
    35. break;
    36. case BODY:
    37. incoming->buffer->data[incoming->index] = byte;
    38. incoming->index++;
    39. incoming->bytes_remaining--;
    40. size_t bytes_read = hal->read_data(type, (incoming->buffer->data + incoming->index), incoming->bytes_remaining);
    41. incoming->index += bytes_read;
    42. incoming->bytes_remaining -= bytes_read;
    43. incoming->state = incoming->bytes_remaining == 0 ? FINISHED : incoming->state;
    44. break;
    45. case IGNORE:
    46. incoming->bytes_remaining--;
    47. if (incoming->bytes_remaining == 0) {
    48. incoming->state = BRAND_NEW;
    49. // Don't forget to let the hal know we finished the packet we were ignoring.
    50. // Otherwise we'll get out of sync with hals that embed extra information
    51. // in the uart stream (like H4). #badnewsbears
    52. hal->packet_finished(type);
    53. return;
    54. }
    55. break;
    56. case FINISHED:
    57. LOG_ERROR(LOG_TAG, "%s the state machine should not have been left in the finished state.", __func__);
    58. break;
    59. }
    60. if (incoming->state == FINISHED) {
    61. incoming->buffer->len = incoming->index;
    62. btsnoop->capture(incoming->buffer, true);
    63. if (type != DATA_TYPE_EVENT) {
    64. packet_fragmenter->reassemble_and_dispatch(incoming->buffer);
    65. } else if (!filter_incoming_event(incoming->buffer)) {
    66. // Dispatch the event by event code
    67. uint8_t *stream = incoming->buffer->data;
    68. uint8_t event_code;
    69. STREAM_TO_UINT8(event_code, stream);
    70. data_dispatcher_dispatch(
    71. interface.event_dispatcher,
    72. event_code,
    73. incoming->buffer
    74. );
    75. }
    76. // We don't control the buffer anymore
    77. incoming->buffer = NULL;
    78. incoming->state = BRAND_NEW;
    79. hal->packet_finished(type);
    80. // We return after a packet is finished for two reasons:
    81. // 1. The type of the next packet could be different.
    82. // 2. We don't want to hog cpu time.
    83. return;
    84. }
    85. }
    86. }

  • 相关阅读:
    【附源码】计算机毕业设计SSM软考刷题系统
    Git:使用conda命令切换虚拟环境
    解读ESSumm: Extractive Speech Summarization from Untranscribed Meeting
    SD6.24集训总结
    JAVA this关键词作用
    Appium+Pytest+Allure实现APP自动化测试,小试牛刀
    Collectors.toMap的暗坑与避免方式
    产品经理进阶:阻碍产品上市的5个因素
    架构师的 36 项修炼第08讲:高可用系统架构设计
    排序算法动图
  • 原文地址:https://blog.csdn.net/XiaoXiaoPengBo/article/details/127336369