Loading system/bta/include/bta_hh_co.h +17 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ #ifndef BTA_HH_CO_H #define BTA_HH_CO_H #include <linux/uhid.h> #include <cstdint> #include "bta/include/bta_hh_api.h" Loading @@ -37,6 +39,21 @@ typedef struct { uint16_t char_inst_id; } tBTA_HH_RPT_CACHE_ENTRY; typedef enum : uint8_t { BTA_HH_UHID_INBOUND_INPUT_EVT, BTA_HH_UHID_INBOUND_CLOSE_EVT, BTA_HH_UHID_INBOUND_DSCP_EVT, BTA_HH_UHID_INBOUND_GET_REPORT_EVT, BTA_HH_UHID_INBOUND_SET_REPORT_EVT, } tBTA_HH_UHID_INBOUND_EVT_TYPE; typedef struct { tBTA_HH_UHID_INBOUND_EVT_TYPE type; union { uhid_event uhid; }; } tBTA_HH_TO_UHID_EVT; /******************************************************************************* * * Function bta_hh_co_data Loading system/btif/co/bta_hh_co.cc +397 −73 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <pthread.h> #include <stdint.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #include <cerrno> Loading @@ -47,7 +48,10 @@ static tBTA_HH_RPT_CACHE_ENTRY sReportCache[BTA_HH_NV_LOAD_MAX]; #define BTA_HH_CACHE_REPORT_VERSION 1 #define THREAD_NORMAL_PRIORITY 0 #define BT_HH_THREAD_PREFIX "bt_hh_" /* poll timeout without the aflags hid_report_queuing */ #define BTA_HH_UHID_POLL_PERIOD_MS 50 /* poll timeout with the aflags hid_report_queuing. -1 indicates no timeout. */ #define BTA_HH_UHID_POLL_PERIOD2_MS -1 /* Max number of polling interrupt allowed */ #define BTA_HH_UHID_INTERRUPT_COUNT_MAX 100 Loading Loading @@ -142,13 +146,47 @@ static int uhid_write(int fd, const struct uhid_event* ev) { return 0; } /* Internal function to parse the events received from UHID driver*/ static int uhid_read_event(btif_hh_uhid_t* p_uhid) { log::assert_that(p_uhid != nullptr, "assert failed: p_uhid != nullptr"); static void uhid_flush_input_queue(btif_hh_uhid_t* p_uhid) { struct uhid_event* p_ev = nullptr; while (true) { p_ev = (struct uhid_event*)fixed_queue_try_dequeue(p_uhid->input_queue); if (p_ev == nullptr) { break; } uhid_write(p_uhid->fd, p_ev); osi_free(p_ev); } } static void uhid_on_open(btif_hh_uhid_t* p_uhid) { if (p_uhid->ready_for_data) { return; } // TODO: handle the case when UHID is still not ready even after sending // UHID_OPEN event, e.g. a custom delay. p_uhid->ready_for_data = true; uhid_flush_input_queue(p_uhid); } struct uhid_event ev; memset(&ev, 0, sizeof(ev)); static void uhid_queue_input(btif_hh_uhid_t* p_uhid, struct uhid_event* ev) { struct uhid_event* p_ev = (struct uhid_event*)osi_malloc(sizeof(*ev)); if (!p_ev) { log::error("allocate uhid_event failed"); return; } memcpy(p_ev, ev, sizeof(*p_ev)); if (!fixed_queue_try_enqueue(p_uhid->input_queue, (void*)p_ev)) { osi_free(p_ev); log::error("uhid_event_queue is full, dropping event"); } } /* Parse the events received from UHID driver*/ static int uhid_read_outbound_event(btif_hh_uhid_t* p_uhid) { log::assert_that(p_uhid != nullptr, "assert failed: p_uhid != nullptr"); struct uhid_event ev = {}; ssize_t ret; OSI_NO_INTR(ret = read(p_uhid->fd, &ev, sizeof(ev))); Loading @@ -163,15 +201,25 @@ static int uhid_read_event(btif_hh_uhid_t* p_uhid) { switch (ev.type) { case UHID_START: log::verbose("UHID_START from uhid-dev\n"); if (!com::android::bluetooth::flags::hid_report_queuing()) { // we can ignore START event, no one is ready to listen anyway. p_uhid->ready_for_data = true; } break; case UHID_STOP: log::verbose("UHID_STOP from uhid-dev\n"); if (!com::android::bluetooth::flags::hid_report_queuing()) { // we can ignore STOP event, it needs to be closed first anyway. p_uhid->ready_for_data = false; } break; case UHID_OPEN: log::verbose("UHID_OPEN from uhid-dev\n"); if (com::android::bluetooth::flags::hid_report_queuing()) { uhid_on_open(p_uhid); } else { p_uhid->ready_for_data = true; } break; case UHID_CLOSE: log::verbose("UHID_CLOSE from uhid-dev\n"); Loading Loading @@ -240,6 +288,69 @@ static int uhid_read_event(btif_hh_uhid_t* p_uhid) { return 0; } // Parse the internal events received from BTIF and translate to UHID // returns -errno when error, 0 when successful, 1 when receiving close event. static int uhid_read_inbound_event(btif_hh_uhid_t* p_uhid) { log::assert_that(p_uhid != nullptr, "assert failed: p_uhid != nullptr"); tBTA_HH_TO_UHID_EVT ev = {}; ssize_t ret; OSI_NO_INTR(ret = read(p_uhid->internal_recv_fd, &ev, sizeof(ev))); if (ret == 0) { log::error("Read HUP on internal uhid-cdev {}", strerror(errno)); return -EFAULT; } else if (ret < 0) { log::error("Cannot read internal uhid-cdev: {}", strerror(errno)); return -errno; } int res = 0; uint32_t* context; switch (ev.type) { case BTA_HH_UHID_INBOUND_INPUT_EVT: if (p_uhid->ready_for_data) { res = uhid_write(p_uhid->fd, &ev.uhid); } else { uhid_queue_input(p_uhid, &ev.uhid); } break; case BTA_HH_UHID_INBOUND_CLOSE_EVT: res = 1; // any positive value indicates a normal close event break; case BTA_HH_UHID_INBOUND_DSCP_EVT: res = uhid_write(p_uhid->fd, &ev.uhid); osi_free(ev.uhid.u.create.rd_data); break; case BTA_HH_UHID_INBOUND_GET_REPORT_EVT: context = (uint32_t*)fixed_queue_try_dequeue(p_uhid->get_rpt_id_queue); if (context == nullptr) { log::warn("No pending UHID_GET_REPORT"); break; } ev.uhid.u.feature_answer.id = *context; res = uhid_write(p_uhid->fd, &ev.uhid); osi_free(context); break; #if ENABLE_UHID_SET_REPORT case BTA_HH_UHID_INBOUND_SET_REPORT_EVT: context = (uint32_t*)fixed_queue_try_dequeue(p_uhid->set_rpt_id_queue); if (context == nullptr) { log::warn("No pending UHID_SET_REPORT"); break; } ev.uhid.u.set_report_reply.id = *context; res = uhid_write(p_uhid->fd, &ev.uhid); osi_free(context); break; #endif // ENABLE_UHID_SET_REPORT default: log::error("Invalid event from internal uhid-dev: {}", (uint8_t)ev.type); } return res; } /******************************************************************************* * * Function create_thread Loading Loading @@ -273,11 +384,33 @@ static void uhid_fd_close(btif_hh_uhid_t* p_uhid) { log::debug("Closing fd={}, addr:{}", p_uhid->fd, p_uhid->link_spec); close(p_uhid->fd); p_uhid->fd = -1; if (!com::android::bluetooth::flags::hid_report_queuing()) { return; } close(p_uhid->internal_recv_fd); p_uhid->internal_recv_fd = -1; /* Clear the queues */ fixed_queue_flush(p_uhid->get_rpt_id_queue, osi_free); fixed_queue_free(p_uhid->get_rpt_id_queue, NULL); p_uhid->get_rpt_id_queue = NULL; #if ENABLE_UHID_SET_REPORT fixed_queue_flush(p_uhid->set_rpt_id_queue, osi_free); fixed_queue_free(p_uhid->set_rpt_id_queue, nullptr); p_uhid->set_rpt_id_queue = nullptr; #endif // ENABLE_UHID_SET_REPORT fixed_queue_flush(p_uhid->input_queue, osi_free); fixed_queue_free(p_uhid->input_queue, nullptr); p_uhid->input_queue = nullptr; osi_free(p_uhid); } } /* Internal function to open the UHID driver*/ static bool uhid_fd_open(btif_hh_device_t* p_dev) { if (!com::android::bluetooth::flags::hid_report_queuing()) { if (p_dev->uhid.fd < 0) { p_dev->uhid.fd = open(dev_path, O_RDWR | O_CLOEXEC); if (p_dev->uhid.fd < 0) { Loading @@ -293,12 +426,31 @@ static bool uhid_fd_open(btif_hh_device_t* p_dev) { return true; } static int uhid_fd_poll(btif_hh_uhid_t* p_uhid, std::array<struct pollfd, 1>& pfds) { if (p_dev->internal_send_fd < 0) { int sockets[2]; if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, sockets) < 0) { return false; } btif_hh_uhid_t* uhid = (btif_hh_uhid_t*)osi_malloc(sizeof(btif_hh_uhid_t)); uhid->link_spec = p_dev->link_spec; uhid->dev_handle = p_dev->dev_handle; uhid->internal_recv_fd = sockets[0]; p_dev->internal_send_fd = sockets[1]; // UHID thread owns the uhid struct and is responsible to free it. p_dev->hh_poll_thread_id = create_thread(btif_hh_poll_event_thread, uhid); } return true; } static int uhid_fd_poll(btif_hh_uhid_t* p_uhid, struct pollfd* pfds, int nfds) { int ret = 0; int counter = 0; do { if (com::android::bluetooth::flags::break_uhid_polling_early() && !p_uhid->hh_keep_polling) { if (com::android::bluetooth::flags::break_uhid_polling_early() && !com::android::bluetooth::flags::hid_report_queuing() && !p_uhid->hh_keep_polling) { log::debug("Polling stopped"); return -1; } Loading @@ -308,7 +460,10 @@ static int uhid_fd_poll(btif_hh_uhid_t* p_uhid, std::array<struct pollfd, 1>& pf return -1; } ret = poll(pfds.data(), pfds.size(), BTA_HH_UHID_POLL_PERIOD_MS); int uhid_poll_timeout = com::android::bluetooth::flags::hid_report_queuing() ? BTA_HH_UHID_POLL_PERIOD2_MS : BTA_HH_UHID_POLL_PERIOD_MS; ret = poll(pfds, nfds, uhid_poll_timeout); } while (ret == -1 && errno == EINTR); if (!com::android::bluetooth::flags::break_uhid_polling_early()) { Loading @@ -322,12 +477,13 @@ static int uhid_fd_poll(btif_hh_uhid_t* p_uhid, std::array<struct pollfd, 1>& pf } static void uhid_start_polling(btif_hh_uhid_t* p_uhid) { if (!com::android::bluetooth::flags::hid_report_queuing()) { std::array<struct pollfd, 1> pfds = {}; pfds[0].fd = p_uhid->fd; pfds[0].events = POLLIN; while (p_uhid->hh_keep_polling) { int ret = uhid_fd_poll(p_uhid, pfds); int ret = uhid_fd_poll(p_uhid, pfds.data(), 1); if (ret < 0) { log::error("Cannot poll for fds: {}\n", strerror(errno)); Loading @@ -340,13 +496,54 @@ static void uhid_start_polling(btif_hh_uhid_t* p_uhid) { /* At least one of the fd is ready */ if (pfds[0].revents & POLLIN) { log::verbose("POLLIN"); int result = uhid_read_event(p_uhid); int result = uhid_read_outbound_event(p_uhid); if (result != 0) { log::error("Unhandled UHID event, error: {}", result); break; } } } return; } std::array<struct pollfd, 2> pfds = {}; pfds[0].fd = p_uhid->fd; pfds[0].events = POLLIN; pfds[1].fd = p_uhid->internal_recv_fd; pfds[1].events = POLLIN; while (true) { int ret = uhid_fd_poll(p_uhid, pfds.data(), 2); if (ret < 0) { log::error("Cannot poll for fds: {}\n", strerror(errno)); break; } if (pfds[0].revents & POLLIN) { log::verbose("POLLIN"); int result = uhid_read_outbound_event(p_uhid); if (result != 0) { log::error("Unhandled UHID outbound event, error: {}", result); break; } } if (pfds[1].revents & POLLIN) { int result = uhid_read_inbound_event(p_uhid); if (result != 0) { if (result < 0) { log::error("Unhandled UHID inbound event, error: {}", result); } break; } } if (pfds[1].revents & POLLHUP) { log::error("inbound fd hangup, disconnect UHID"); break; } } } static bool uhid_configure_thread(btif_hh_uhid_t* p_uhid) { Loading Loading @@ -385,22 +582,65 @@ static bool uhid_configure_thread(btif_hh_uhid_t* p_uhid) { static void* btif_hh_poll_event_thread(void* arg) { btif_hh_uhid_t* p_uhid = (btif_hh_uhid_t*)arg; if (com::android::bluetooth::flags::hid_report_queuing()) { p_uhid->fd = open(dev_path, O_RDWR | O_CLOEXEC); if (p_uhid->fd < 0) { log::error("Failed to open uhid, err:{}", strerror(errno)); close(p_uhid->internal_recv_fd); p_uhid->internal_recv_fd = -1; return 0; } p_uhid->ready_for_data = false; p_uhid->get_rpt_id_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_uhid->get_rpt_id_queue, "assert failed: p_uhid->get_rpt_id_queue"); #if ENABLE_UHID_SET_REPORT p_uhid->set_rpt_id_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_uhid->set_rpt_id_queue, "assert failed: p_uhid->set_rpt_id_queue"); #endif // ENABLE_UHID_SET_REPORT p_uhid->input_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_uhid->input_queue, "assert failed: p_uhid->input_queue"); } if (uhid_configure_thread(p_uhid)) { uhid_start_polling(p_uhid); } /* Todo: Disconnect if loop exited due to a failure */ log::info("Polling thread stopped for device {}", p_uhid->link_spec); if (!com::android::bluetooth::flags::hid_report_queuing()) { p_uhid->hh_keep_polling = 0; } uhid_fd_close(p_uhid); return 0; } /* Pass messages to be handled by uhid_read_inbound_event in the UHID thread */ static bool to_uhid_thread(int fd, const tBTA_HH_TO_UHID_EVT* ev) { if (fd < 0) { log::error("Cannot write to uhid thread: invalid fd"); return false; } ssize_t ret; OSI_NO_INTR(ret = write(fd, ev, sizeof(*ev))); if (ret < 0) { log::error("Cannot write to uhid thread: {}", strerror(errno)); return false; } else if (ret != (ssize_t)sizeof(*ev)) { log::error("Wrong size written to uhid thread: {} != {}", ret, sizeof(*ev)); return false; } return true; } int bta_hh_co_write(int fd, uint8_t* rpt, uint16_t len) { log::verbose("UHID write {}", len); struct uhid_event ev; memset(&ev, 0, sizeof(ev)); tBTA_HH_TO_UHID_EVT to_uhid = {}; struct uhid_event& ev = to_uhid.uhid; ev.type = UHID_INPUT; ev.u.input.size = len; if (len > sizeof(ev.u.input.data)) { Loading @@ -409,9 +649,14 @@ int bta_hh_co_write(int fd, uint8_t* rpt, uint16_t len) { } memcpy(ev.u.input.data, rpt, len); if (!com::android::bluetooth::flags::hid_report_queuing()) { return uhid_write(fd, &ev); } to_uhid.type = BTA_HH_UHID_INBOUND_INPUT_EVT; return to_uhid_thread(fd, &to_uhid) ? 0 : -1; } /******************************************************************************* * * Function bta_hh_co_open Loading Loading @@ -449,16 +694,26 @@ bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK att new_device = true; log::verbose("New HID device added for handle {}", dev_handle); if (com::android::bluetooth::flags::hid_report_queuing()) { p_dev->internal_send_fd = -1; } else { p_dev->uhid.fd = -1; p_dev->uhid.hh_keep_polling = 0; p_dev->uhid.link_spec = link_spec; p_dev->uhid.dev_handle = dev_handle; } p_dev->attr_mask = attr_mask; p_dev->sub_class = sub_class; p_dev->app_id = app_id; p_dev->local_vup = false; } if (com::android::bluetooth::flags::hid_report_queuing()) { p_dev->link_spec = link_spec; p_dev->dev_handle = dev_handle; } else { p_dev->uhid.link_spec = link_spec; p_dev->uhid.dev_handle = dev_handle; } if (!uhid_fd_open(p_dev)) { return false; } Loading @@ -468,6 +723,8 @@ bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK att } p_dev->dev_status = BTHH_CONN_STATE_CONNECTED; if (!com::android::bluetooth::flags::hid_report_queuing()) { p_dev->dev_handle = dev_handle; p_dev->uhid.get_rpt_id_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_dev->uhid.get_rpt_id_queue, "assert failed: p_dev->uhid.get_rpt_id_queue"); Loading @@ -475,6 +732,7 @@ bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK att p_dev->uhid.set_rpt_id_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_dev->uhid.set_rpt_id_queue, "assert failed: p_dev->uhid.set_rpt_id_queue"); #endif // ENABLE_UHID_SET_REPORT } log::debug("Return device status {}", p_dev->dev_status); return true; Loading @@ -495,6 +753,7 @@ void bta_hh_co_close(btif_hh_device_t* p_dev) { log::info("Closing device handle={}, status={}, address={}", p_dev->dev_handle, p_dev->dev_status, p_dev->link_spec); if (!com::android::bluetooth::flags::hid_report_queuing()) { /* Clear the queues */ fixed_queue_flush(p_dev->uhid.get_rpt_id_queue, osi_free); fixed_queue_free(p_dev->uhid.get_rpt_id_queue, NULL); Loading @@ -512,6 +771,20 @@ void bta_hh_co_close(btif_hh_device_t* p_dev) { p_dev->hh_poll_thread_id = -1; } /* UHID file descriptor is closed by the polling thread */ return; } if (p_dev->internal_send_fd >= 0) { tBTA_HH_TO_UHID_EVT to_uhid = {}; to_uhid.type = BTA_HH_UHID_INBOUND_CLOSE_EVT; to_uhid_thread(p_dev->internal_send_fd, &to_uhid); pthread_join(p_dev->hh_poll_thread_id, NULL); p_dev->hh_poll_thread_id = -1; close(p_dev->internal_send_fd); p_dev->internal_send_fd = -1; } } /******************************************************************************* Loading @@ -538,6 +811,11 @@ void bta_hh_co_data(uint8_t dev_handle, uint8_t* p_rpt, uint16_t len) { return; } if (com::android::bluetooth::flags::hid_report_queuing()) { bta_hh_co_write(p_dev->internal_send_fd, p_rpt, len); return; } // Wait a maximum of MAX_POLLING_ATTEMPTS x POLLING_SLEEP_DURATION in case // device creation is pending. if (p_dev->uhid.fd >= 0) { Loading Loading @@ -573,21 +851,23 @@ void bta_hh_co_send_hid_info(btif_hh_device_t* p_dev, const char* dev_name, uint uint16_t product_id, uint16_t version, uint8_t ctry_code, int dscp_len, uint8_t* p_dscp) { int result; struct uhid_event ev; tBTA_HH_TO_UHID_EVT to_uhid = {}; struct uhid_event& ev = to_uhid.uhid; if (!com::android::bluetooth::flags::hid_report_queuing()) { if (p_dev->uhid.fd < 0) { log::warn("Error: fd = {}, dscp_len = {}", p_dev->uhid.fd, dscp_len); return; } log::warn("fd = {}, name = [{}], dscp_len = {}", p_dev->uhid.fd, dev_name, dscp_len); log::warn( } log::info( "vendor_id = 0x{:04x}, product_id = 0x{:04x}, version= " "0x{:04x},ctry_code=0x{:02x}", vendor_id, product_id, version, ctry_code); // Create and send hid descriptor to kernel memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; strlcpy((char*)ev.u.create.name, dev_name, sizeof(ev.u.create.name)); // TODO (b/258090765) fix: ToString -> ToColonSepHexString Loading @@ -608,6 +888,8 @@ void bta_hh_co_send_hid_info(btif_hh_device_t* p_dev, const char* dev_name, uint ev.u.create.product = product_id; ev.u.create.version = version; ev.u.create.country = ctry_code; if (!com::android::bluetooth::flags::hid_report_queuing()) { result = uhid_write(p_dev->uhid.fd, &ev); log::warn("wrote descriptor to fd = {}, dscp_len = {}, result = {}", p_dev->uhid.fd, dscp_len, Loading @@ -620,6 +902,26 @@ void bta_hh_co_send_hid_info(btif_hh_device_t* p_dev, const char* dev_name, uint close(p_dev->uhid.fd); p_dev->uhid.fd = -1; } return; } to_uhid.type = BTA_HH_UHID_INBOUND_DSCP_EVT; ev.u.create.rd_data = (uint8_t*)osi_malloc(ev.u.create.rd_size); memcpy(ev.u.create.rd_data, p_dscp, ev.u.create.rd_size); if (!to_uhid_thread(p_dev->internal_send_fd, &to_uhid)) { log::warn("Error: failed to send DSCP"); if (p_dev->internal_send_fd >= 0) { // Detach the uhid thread. It will exit by itself upon receiving hangup. pthread_detach(p_dev->hh_poll_thread_id); p_dev->hh_poll_thread_id = -1; close(p_dev->internal_send_fd); p_dev->internal_send_fd = -1; } } return; } /******************************************************************************* Loading @@ -642,6 +944,16 @@ void bta_hh_co_set_rpt_rsp(uint8_t dev_handle, uint8_t status) { return; } if (com::android::bluetooth::flags::hid_report_queuing()) { tBTA_HH_TO_UHID_EVT to_uhid = {}; to_uhid.type = BTA_HH_UHID_INBOUND_SET_REPORT_EVT; to_uhid.uhid.type = UHID_SET_REPORT_REPLY; to_uhid.uhid.u.set_report_reply.err = status; to_uhid_thread(p_dev->internal_send_fd, &to_uhid); return; } if (!p_dev->uhid.set_rpt_id_queue) { log::warn("Missing UHID_SET_REPORT id queue"); return; Loading Loading @@ -700,6 +1012,23 @@ void bta_hh_co_get_rpt_rsp(uint8_t dev_handle, uint8_t status, const uint8_t* p_ return; } if (len == 0 || len > UHID_DATA_MAX) { log::warn("Invalid report size = {}", len); return; } if (com::android::bluetooth::flags::hid_report_queuing()) { tBTA_HH_TO_UHID_EVT to_uhid = {}; to_uhid.type = BTA_HH_UHID_INBOUND_GET_REPORT_EVT; to_uhid.uhid.type = UHID_FEATURE_ANSWER; to_uhid.uhid.u.feature_answer.err = status; to_uhid.uhid.u.feature_answer.size = len; memcpy(to_uhid.uhid.u.feature_answer.data, p_rpt, len); to_uhid_thread(p_dev->internal_send_fd, &to_uhid); return; } if (!p_dev->uhid.get_rpt_id_queue) { log::warn("Missing UHID_GET_REPORT id queue"); return; Loading @@ -718,11 +1047,6 @@ void bta_hh_co_get_rpt_rsp(uint8_t dev_handle, uint8_t status, const uint8_t* p_ return; } if (len == 0 || len > UHID_DATA_MAX) { log::warn("Invalid report size = {}", len); return; } struct uhid_event ev = { .type = UHID_FEATURE_ANSWER, .u = Loading system/btif/include/btif_hh.h +9 −5 Original line number Diff line number Diff line Loading @@ -84,18 +84,20 @@ inline std::string btif_hh_status_text(const BTIF_HH_STATUS& status) { } } /* Supposedly is exclusive to uhid thread, but now is still accessed by btif. */ /* TODO: remove btif_hh_uhid_t from btif_hh_device_t. */ /* Uhid thread has exclusive access to this block. */ typedef struct { int fd; int fd; // for interfacing with uhid int internal_recv_fd; // for receiving internal events in uhid thread uint8_t dev_handle; tAclLinkSpec link_spec; uint8_t hh_keep_polling; uint8_t hh_keep_polling; // Deprecated with the aflags hid_report_queuing. // TODO: remove after launching the aflag. bool ready_for_data; fixed_queue_t* get_rpt_id_queue; #if ENABLE_UHID_SET_REPORT fixed_queue_t* set_rpt_id_queue; #endif // ENABLE_UHID_SET_REPORT fixed_queue_t* input_queue; // to store the inputs before uhid is ready. } btif_hh_uhid_t; /* Control block to maintain properties of devices */ Loading @@ -106,10 +108,12 @@ typedef struct { tBTA_HH_ATTR_MASK attr_mask; uint8_t sub_class; uint8_t app_id; int internal_send_fd; // for sending internal events from btif pthread_t hh_poll_thread_id; alarm_t* vup_timer; bool local_vup; // Indicated locally initiated VUP btif_hh_uhid_t uhid; btif_hh_uhid_t uhid; // Deprecated with the aflags hid_report_queuing. // TODO: remove after launching the aflag. } btif_hh_device_t; /* Control block to maintain properties of devices */ Loading system/btif/src/btif_hh.cc +25 −11 Original line number Diff line number Diff line Loading @@ -295,7 +295,9 @@ static void sync_lockstate_on_connect(btif_hh_device_t* p_dev) { if (keylockstates) { log::verbose("Sending hid report to kernel indicating lock key state 0x{:x}", keylockstates); usleep(200000); toggle_os_keylockstates(p_dev->uhid.fd, keylockstates); int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); toggle_os_keylockstates(fd, keylockstates); } else { log::verbose("NOT sending hid report to kernel indicating lock key state 0x{:x}", keylockstates); Loading Loading @@ -557,8 +559,11 @@ static void hh_open_handler(tBTA_HH_CONN& conn) { log::info("Found device, getting dscp info for handle {}", conn.handle); if (!com::android::bluetooth::flags::hid_report_queuing()) { // link_spec and status is to be set in bta_hh_co_open instead. p_dev->link_spec = conn.link_spec; p_dev->dev_status = BTHH_CONN_STATE_CONNECTED; } hh_connect_complete(conn.handle, conn.link_spec, BTIF_HH_DEV_CONNECTED); // Send set_idle if the peer_device is a keyboard if (check_cod_hid_major(conn.link_spec.addrt.bda, COD_HID_KEYBOARD) || Loading Loading @@ -686,9 +691,11 @@ void btif_hh_remove_device(const tAclLinkSpec& link_spec) { bta_hh_co_close(p_dev); p_dev->dev_status = BTHH_CONN_STATE_UNKNOWN; p_dev->dev_handle = BTA_HH_INVALID_HANDLE; if (!com::android::bluetooth::flags::hid_report_queuing()) { p_dev->uhid.ready_for_data = false; } } } bool btif_hh_copy_hid_info(tBTA_HH_DEV_DSCP_INFO* dest, tBTA_HH_DEV_DSCP_INFO* src) { memset(dest, 0, sizeof(tBTA_HH_DEV_DSCP_INFO)); Loading Loading @@ -1000,8 +1007,10 @@ static void btif_hh_upstreams_evt(uint16_t event, char* p_param) { p_data->dev_status.handle); p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle); if (p_dev != NULL) { int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); BTHH_STATE_UPDATE(p_dev->link_spec, BTHH_CONN_STATE_DISCONNECTING); log::verbose("uhid fd={} local_vup={}", p_dev->uhid.fd, p_dev->local_vup); log::verbose("uhid fd={} local_vup={}", fd, p_dev->local_vup); btif_hh_stop_vup_timer(p_dev->link_spec); /* If this is a locally initiated VUP, remove the bond as ACL got * disconnected while VUP being processed. Loading Loading @@ -1131,7 +1140,9 @@ static void btif_hh_upstreams_evt(uint16_t event, char* p_param) { return; } if (p_dev->uhid.fd < 0) { int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); if (fd < 0) { log::error("BTA_HH_GET_DSCP_EVT: failed to find the uhid driver..."); return; } Loading Loading @@ -2042,8 +2053,10 @@ static void cleanup(void) { } for (i = 0; i < BTIF_HH_MAX_HID; i++) { p_dev = &btif_hh_cb.devices[i]; if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN && p_dev->uhid.fd >= 0) { log::verbose("Closing uhid fd = {}", p_dev->uhid.fd); int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN && fd >= 0) { log::verbose("Closing uhid fd = {}", fd); bta_hh_co_close(p_dev); } } Loading Loading @@ -2126,10 +2139,11 @@ void DumpsysHid(int fd) { for (unsigned i = 0; i < BTIF_HH_MAX_HID; i++) { const btif_hh_device_t* p_dev = &btif_hh_cb.devices[i]; if (p_dev->link_spec.addrt.bda != RawAddress::kEmpty) { LOG_DUMPSYS(fd, " %u: addr:%s fd:%d state:%s ready:%s thread_id:%d handle:%d", i, p_dev->link_spec.ToRedactedStringForLogging().c_str(), p_dev->uhid.fd, int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); LOG_DUMPSYS(fd, " %u: addr:%s fd:%d state:%s thread_id:%d handle:%d", i, p_dev->link_spec.ToRedactedStringForLogging().c_str(), fd, bthh_connection_state_text(p_dev->dev_status).c_str(), (p_dev->uhid.ready_for_data) ? ("T") : ("F"), static_cast<int>(p_dev->hh_poll_thread_id), p_dev->dev_handle); } } Loading Loading
system/bta/include/bta_hh_co.h +17 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ #ifndef BTA_HH_CO_H #define BTA_HH_CO_H #include <linux/uhid.h> #include <cstdint> #include "bta/include/bta_hh_api.h" Loading @@ -37,6 +39,21 @@ typedef struct { uint16_t char_inst_id; } tBTA_HH_RPT_CACHE_ENTRY; typedef enum : uint8_t { BTA_HH_UHID_INBOUND_INPUT_EVT, BTA_HH_UHID_INBOUND_CLOSE_EVT, BTA_HH_UHID_INBOUND_DSCP_EVT, BTA_HH_UHID_INBOUND_GET_REPORT_EVT, BTA_HH_UHID_INBOUND_SET_REPORT_EVT, } tBTA_HH_UHID_INBOUND_EVT_TYPE; typedef struct { tBTA_HH_UHID_INBOUND_EVT_TYPE type; union { uhid_event uhid; }; } tBTA_HH_TO_UHID_EVT; /******************************************************************************* * * Function bta_hh_co_data Loading
system/btif/co/bta_hh_co.cc +397 −73 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <pthread.h> #include <stdint.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #include <cerrno> Loading @@ -47,7 +48,10 @@ static tBTA_HH_RPT_CACHE_ENTRY sReportCache[BTA_HH_NV_LOAD_MAX]; #define BTA_HH_CACHE_REPORT_VERSION 1 #define THREAD_NORMAL_PRIORITY 0 #define BT_HH_THREAD_PREFIX "bt_hh_" /* poll timeout without the aflags hid_report_queuing */ #define BTA_HH_UHID_POLL_PERIOD_MS 50 /* poll timeout with the aflags hid_report_queuing. -1 indicates no timeout. */ #define BTA_HH_UHID_POLL_PERIOD2_MS -1 /* Max number of polling interrupt allowed */ #define BTA_HH_UHID_INTERRUPT_COUNT_MAX 100 Loading Loading @@ -142,13 +146,47 @@ static int uhid_write(int fd, const struct uhid_event* ev) { return 0; } /* Internal function to parse the events received from UHID driver*/ static int uhid_read_event(btif_hh_uhid_t* p_uhid) { log::assert_that(p_uhid != nullptr, "assert failed: p_uhid != nullptr"); static void uhid_flush_input_queue(btif_hh_uhid_t* p_uhid) { struct uhid_event* p_ev = nullptr; while (true) { p_ev = (struct uhid_event*)fixed_queue_try_dequeue(p_uhid->input_queue); if (p_ev == nullptr) { break; } uhid_write(p_uhid->fd, p_ev); osi_free(p_ev); } } static void uhid_on_open(btif_hh_uhid_t* p_uhid) { if (p_uhid->ready_for_data) { return; } // TODO: handle the case when UHID is still not ready even after sending // UHID_OPEN event, e.g. a custom delay. p_uhid->ready_for_data = true; uhid_flush_input_queue(p_uhid); } struct uhid_event ev; memset(&ev, 0, sizeof(ev)); static void uhid_queue_input(btif_hh_uhid_t* p_uhid, struct uhid_event* ev) { struct uhid_event* p_ev = (struct uhid_event*)osi_malloc(sizeof(*ev)); if (!p_ev) { log::error("allocate uhid_event failed"); return; } memcpy(p_ev, ev, sizeof(*p_ev)); if (!fixed_queue_try_enqueue(p_uhid->input_queue, (void*)p_ev)) { osi_free(p_ev); log::error("uhid_event_queue is full, dropping event"); } } /* Parse the events received from UHID driver*/ static int uhid_read_outbound_event(btif_hh_uhid_t* p_uhid) { log::assert_that(p_uhid != nullptr, "assert failed: p_uhid != nullptr"); struct uhid_event ev = {}; ssize_t ret; OSI_NO_INTR(ret = read(p_uhid->fd, &ev, sizeof(ev))); Loading @@ -163,15 +201,25 @@ static int uhid_read_event(btif_hh_uhid_t* p_uhid) { switch (ev.type) { case UHID_START: log::verbose("UHID_START from uhid-dev\n"); if (!com::android::bluetooth::flags::hid_report_queuing()) { // we can ignore START event, no one is ready to listen anyway. p_uhid->ready_for_data = true; } break; case UHID_STOP: log::verbose("UHID_STOP from uhid-dev\n"); if (!com::android::bluetooth::flags::hid_report_queuing()) { // we can ignore STOP event, it needs to be closed first anyway. p_uhid->ready_for_data = false; } break; case UHID_OPEN: log::verbose("UHID_OPEN from uhid-dev\n"); if (com::android::bluetooth::flags::hid_report_queuing()) { uhid_on_open(p_uhid); } else { p_uhid->ready_for_data = true; } break; case UHID_CLOSE: log::verbose("UHID_CLOSE from uhid-dev\n"); Loading Loading @@ -240,6 +288,69 @@ static int uhid_read_event(btif_hh_uhid_t* p_uhid) { return 0; } // Parse the internal events received from BTIF and translate to UHID // returns -errno when error, 0 when successful, 1 when receiving close event. static int uhid_read_inbound_event(btif_hh_uhid_t* p_uhid) { log::assert_that(p_uhid != nullptr, "assert failed: p_uhid != nullptr"); tBTA_HH_TO_UHID_EVT ev = {}; ssize_t ret; OSI_NO_INTR(ret = read(p_uhid->internal_recv_fd, &ev, sizeof(ev))); if (ret == 0) { log::error("Read HUP on internal uhid-cdev {}", strerror(errno)); return -EFAULT; } else if (ret < 0) { log::error("Cannot read internal uhid-cdev: {}", strerror(errno)); return -errno; } int res = 0; uint32_t* context; switch (ev.type) { case BTA_HH_UHID_INBOUND_INPUT_EVT: if (p_uhid->ready_for_data) { res = uhid_write(p_uhid->fd, &ev.uhid); } else { uhid_queue_input(p_uhid, &ev.uhid); } break; case BTA_HH_UHID_INBOUND_CLOSE_EVT: res = 1; // any positive value indicates a normal close event break; case BTA_HH_UHID_INBOUND_DSCP_EVT: res = uhid_write(p_uhid->fd, &ev.uhid); osi_free(ev.uhid.u.create.rd_data); break; case BTA_HH_UHID_INBOUND_GET_REPORT_EVT: context = (uint32_t*)fixed_queue_try_dequeue(p_uhid->get_rpt_id_queue); if (context == nullptr) { log::warn("No pending UHID_GET_REPORT"); break; } ev.uhid.u.feature_answer.id = *context; res = uhid_write(p_uhid->fd, &ev.uhid); osi_free(context); break; #if ENABLE_UHID_SET_REPORT case BTA_HH_UHID_INBOUND_SET_REPORT_EVT: context = (uint32_t*)fixed_queue_try_dequeue(p_uhid->set_rpt_id_queue); if (context == nullptr) { log::warn("No pending UHID_SET_REPORT"); break; } ev.uhid.u.set_report_reply.id = *context; res = uhid_write(p_uhid->fd, &ev.uhid); osi_free(context); break; #endif // ENABLE_UHID_SET_REPORT default: log::error("Invalid event from internal uhid-dev: {}", (uint8_t)ev.type); } return res; } /******************************************************************************* * * Function create_thread Loading Loading @@ -273,11 +384,33 @@ static void uhid_fd_close(btif_hh_uhid_t* p_uhid) { log::debug("Closing fd={}, addr:{}", p_uhid->fd, p_uhid->link_spec); close(p_uhid->fd); p_uhid->fd = -1; if (!com::android::bluetooth::flags::hid_report_queuing()) { return; } close(p_uhid->internal_recv_fd); p_uhid->internal_recv_fd = -1; /* Clear the queues */ fixed_queue_flush(p_uhid->get_rpt_id_queue, osi_free); fixed_queue_free(p_uhid->get_rpt_id_queue, NULL); p_uhid->get_rpt_id_queue = NULL; #if ENABLE_UHID_SET_REPORT fixed_queue_flush(p_uhid->set_rpt_id_queue, osi_free); fixed_queue_free(p_uhid->set_rpt_id_queue, nullptr); p_uhid->set_rpt_id_queue = nullptr; #endif // ENABLE_UHID_SET_REPORT fixed_queue_flush(p_uhid->input_queue, osi_free); fixed_queue_free(p_uhid->input_queue, nullptr); p_uhid->input_queue = nullptr; osi_free(p_uhid); } } /* Internal function to open the UHID driver*/ static bool uhid_fd_open(btif_hh_device_t* p_dev) { if (!com::android::bluetooth::flags::hid_report_queuing()) { if (p_dev->uhid.fd < 0) { p_dev->uhid.fd = open(dev_path, O_RDWR | O_CLOEXEC); if (p_dev->uhid.fd < 0) { Loading @@ -293,12 +426,31 @@ static bool uhid_fd_open(btif_hh_device_t* p_dev) { return true; } static int uhid_fd_poll(btif_hh_uhid_t* p_uhid, std::array<struct pollfd, 1>& pfds) { if (p_dev->internal_send_fd < 0) { int sockets[2]; if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, sockets) < 0) { return false; } btif_hh_uhid_t* uhid = (btif_hh_uhid_t*)osi_malloc(sizeof(btif_hh_uhid_t)); uhid->link_spec = p_dev->link_spec; uhid->dev_handle = p_dev->dev_handle; uhid->internal_recv_fd = sockets[0]; p_dev->internal_send_fd = sockets[1]; // UHID thread owns the uhid struct and is responsible to free it. p_dev->hh_poll_thread_id = create_thread(btif_hh_poll_event_thread, uhid); } return true; } static int uhid_fd_poll(btif_hh_uhid_t* p_uhid, struct pollfd* pfds, int nfds) { int ret = 0; int counter = 0; do { if (com::android::bluetooth::flags::break_uhid_polling_early() && !p_uhid->hh_keep_polling) { if (com::android::bluetooth::flags::break_uhid_polling_early() && !com::android::bluetooth::flags::hid_report_queuing() && !p_uhid->hh_keep_polling) { log::debug("Polling stopped"); return -1; } Loading @@ -308,7 +460,10 @@ static int uhid_fd_poll(btif_hh_uhid_t* p_uhid, std::array<struct pollfd, 1>& pf return -1; } ret = poll(pfds.data(), pfds.size(), BTA_HH_UHID_POLL_PERIOD_MS); int uhid_poll_timeout = com::android::bluetooth::flags::hid_report_queuing() ? BTA_HH_UHID_POLL_PERIOD2_MS : BTA_HH_UHID_POLL_PERIOD_MS; ret = poll(pfds, nfds, uhid_poll_timeout); } while (ret == -1 && errno == EINTR); if (!com::android::bluetooth::flags::break_uhid_polling_early()) { Loading @@ -322,12 +477,13 @@ static int uhid_fd_poll(btif_hh_uhid_t* p_uhid, std::array<struct pollfd, 1>& pf } static void uhid_start_polling(btif_hh_uhid_t* p_uhid) { if (!com::android::bluetooth::flags::hid_report_queuing()) { std::array<struct pollfd, 1> pfds = {}; pfds[0].fd = p_uhid->fd; pfds[0].events = POLLIN; while (p_uhid->hh_keep_polling) { int ret = uhid_fd_poll(p_uhid, pfds); int ret = uhid_fd_poll(p_uhid, pfds.data(), 1); if (ret < 0) { log::error("Cannot poll for fds: {}\n", strerror(errno)); Loading @@ -340,13 +496,54 @@ static void uhid_start_polling(btif_hh_uhid_t* p_uhid) { /* At least one of the fd is ready */ if (pfds[0].revents & POLLIN) { log::verbose("POLLIN"); int result = uhid_read_event(p_uhid); int result = uhid_read_outbound_event(p_uhid); if (result != 0) { log::error("Unhandled UHID event, error: {}", result); break; } } } return; } std::array<struct pollfd, 2> pfds = {}; pfds[0].fd = p_uhid->fd; pfds[0].events = POLLIN; pfds[1].fd = p_uhid->internal_recv_fd; pfds[1].events = POLLIN; while (true) { int ret = uhid_fd_poll(p_uhid, pfds.data(), 2); if (ret < 0) { log::error("Cannot poll for fds: {}\n", strerror(errno)); break; } if (pfds[0].revents & POLLIN) { log::verbose("POLLIN"); int result = uhid_read_outbound_event(p_uhid); if (result != 0) { log::error("Unhandled UHID outbound event, error: {}", result); break; } } if (pfds[1].revents & POLLIN) { int result = uhid_read_inbound_event(p_uhid); if (result != 0) { if (result < 0) { log::error("Unhandled UHID inbound event, error: {}", result); } break; } } if (pfds[1].revents & POLLHUP) { log::error("inbound fd hangup, disconnect UHID"); break; } } } static bool uhid_configure_thread(btif_hh_uhid_t* p_uhid) { Loading Loading @@ -385,22 +582,65 @@ static bool uhid_configure_thread(btif_hh_uhid_t* p_uhid) { static void* btif_hh_poll_event_thread(void* arg) { btif_hh_uhid_t* p_uhid = (btif_hh_uhid_t*)arg; if (com::android::bluetooth::flags::hid_report_queuing()) { p_uhid->fd = open(dev_path, O_RDWR | O_CLOEXEC); if (p_uhid->fd < 0) { log::error("Failed to open uhid, err:{}", strerror(errno)); close(p_uhid->internal_recv_fd); p_uhid->internal_recv_fd = -1; return 0; } p_uhid->ready_for_data = false; p_uhid->get_rpt_id_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_uhid->get_rpt_id_queue, "assert failed: p_uhid->get_rpt_id_queue"); #if ENABLE_UHID_SET_REPORT p_uhid->set_rpt_id_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_uhid->set_rpt_id_queue, "assert failed: p_uhid->set_rpt_id_queue"); #endif // ENABLE_UHID_SET_REPORT p_uhid->input_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_uhid->input_queue, "assert failed: p_uhid->input_queue"); } if (uhid_configure_thread(p_uhid)) { uhid_start_polling(p_uhid); } /* Todo: Disconnect if loop exited due to a failure */ log::info("Polling thread stopped for device {}", p_uhid->link_spec); if (!com::android::bluetooth::flags::hid_report_queuing()) { p_uhid->hh_keep_polling = 0; } uhid_fd_close(p_uhid); return 0; } /* Pass messages to be handled by uhid_read_inbound_event in the UHID thread */ static bool to_uhid_thread(int fd, const tBTA_HH_TO_UHID_EVT* ev) { if (fd < 0) { log::error("Cannot write to uhid thread: invalid fd"); return false; } ssize_t ret; OSI_NO_INTR(ret = write(fd, ev, sizeof(*ev))); if (ret < 0) { log::error("Cannot write to uhid thread: {}", strerror(errno)); return false; } else if (ret != (ssize_t)sizeof(*ev)) { log::error("Wrong size written to uhid thread: {} != {}", ret, sizeof(*ev)); return false; } return true; } int bta_hh_co_write(int fd, uint8_t* rpt, uint16_t len) { log::verbose("UHID write {}", len); struct uhid_event ev; memset(&ev, 0, sizeof(ev)); tBTA_HH_TO_UHID_EVT to_uhid = {}; struct uhid_event& ev = to_uhid.uhid; ev.type = UHID_INPUT; ev.u.input.size = len; if (len > sizeof(ev.u.input.data)) { Loading @@ -409,9 +649,14 @@ int bta_hh_co_write(int fd, uint8_t* rpt, uint16_t len) { } memcpy(ev.u.input.data, rpt, len); if (!com::android::bluetooth::flags::hid_report_queuing()) { return uhid_write(fd, &ev); } to_uhid.type = BTA_HH_UHID_INBOUND_INPUT_EVT; return to_uhid_thread(fd, &to_uhid) ? 0 : -1; } /******************************************************************************* * * Function bta_hh_co_open Loading Loading @@ -449,16 +694,26 @@ bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK att new_device = true; log::verbose("New HID device added for handle {}", dev_handle); if (com::android::bluetooth::flags::hid_report_queuing()) { p_dev->internal_send_fd = -1; } else { p_dev->uhid.fd = -1; p_dev->uhid.hh_keep_polling = 0; p_dev->uhid.link_spec = link_spec; p_dev->uhid.dev_handle = dev_handle; } p_dev->attr_mask = attr_mask; p_dev->sub_class = sub_class; p_dev->app_id = app_id; p_dev->local_vup = false; } if (com::android::bluetooth::flags::hid_report_queuing()) { p_dev->link_spec = link_spec; p_dev->dev_handle = dev_handle; } else { p_dev->uhid.link_spec = link_spec; p_dev->uhid.dev_handle = dev_handle; } if (!uhid_fd_open(p_dev)) { return false; } Loading @@ -468,6 +723,8 @@ bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK att } p_dev->dev_status = BTHH_CONN_STATE_CONNECTED; if (!com::android::bluetooth::flags::hid_report_queuing()) { p_dev->dev_handle = dev_handle; p_dev->uhid.get_rpt_id_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_dev->uhid.get_rpt_id_queue, "assert failed: p_dev->uhid.get_rpt_id_queue"); Loading @@ -475,6 +732,7 @@ bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK att p_dev->uhid.set_rpt_id_queue = fixed_queue_new(SIZE_MAX); log::assert_that(p_dev->uhid.set_rpt_id_queue, "assert failed: p_dev->uhid.set_rpt_id_queue"); #endif // ENABLE_UHID_SET_REPORT } log::debug("Return device status {}", p_dev->dev_status); return true; Loading @@ -495,6 +753,7 @@ void bta_hh_co_close(btif_hh_device_t* p_dev) { log::info("Closing device handle={}, status={}, address={}", p_dev->dev_handle, p_dev->dev_status, p_dev->link_spec); if (!com::android::bluetooth::flags::hid_report_queuing()) { /* Clear the queues */ fixed_queue_flush(p_dev->uhid.get_rpt_id_queue, osi_free); fixed_queue_free(p_dev->uhid.get_rpt_id_queue, NULL); Loading @@ -512,6 +771,20 @@ void bta_hh_co_close(btif_hh_device_t* p_dev) { p_dev->hh_poll_thread_id = -1; } /* UHID file descriptor is closed by the polling thread */ return; } if (p_dev->internal_send_fd >= 0) { tBTA_HH_TO_UHID_EVT to_uhid = {}; to_uhid.type = BTA_HH_UHID_INBOUND_CLOSE_EVT; to_uhid_thread(p_dev->internal_send_fd, &to_uhid); pthread_join(p_dev->hh_poll_thread_id, NULL); p_dev->hh_poll_thread_id = -1; close(p_dev->internal_send_fd); p_dev->internal_send_fd = -1; } } /******************************************************************************* Loading @@ -538,6 +811,11 @@ void bta_hh_co_data(uint8_t dev_handle, uint8_t* p_rpt, uint16_t len) { return; } if (com::android::bluetooth::flags::hid_report_queuing()) { bta_hh_co_write(p_dev->internal_send_fd, p_rpt, len); return; } // Wait a maximum of MAX_POLLING_ATTEMPTS x POLLING_SLEEP_DURATION in case // device creation is pending. if (p_dev->uhid.fd >= 0) { Loading Loading @@ -573,21 +851,23 @@ void bta_hh_co_send_hid_info(btif_hh_device_t* p_dev, const char* dev_name, uint uint16_t product_id, uint16_t version, uint8_t ctry_code, int dscp_len, uint8_t* p_dscp) { int result; struct uhid_event ev; tBTA_HH_TO_UHID_EVT to_uhid = {}; struct uhid_event& ev = to_uhid.uhid; if (!com::android::bluetooth::flags::hid_report_queuing()) { if (p_dev->uhid.fd < 0) { log::warn("Error: fd = {}, dscp_len = {}", p_dev->uhid.fd, dscp_len); return; } log::warn("fd = {}, name = [{}], dscp_len = {}", p_dev->uhid.fd, dev_name, dscp_len); log::warn( } log::info( "vendor_id = 0x{:04x}, product_id = 0x{:04x}, version= " "0x{:04x},ctry_code=0x{:02x}", vendor_id, product_id, version, ctry_code); // Create and send hid descriptor to kernel memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; strlcpy((char*)ev.u.create.name, dev_name, sizeof(ev.u.create.name)); // TODO (b/258090765) fix: ToString -> ToColonSepHexString Loading @@ -608,6 +888,8 @@ void bta_hh_co_send_hid_info(btif_hh_device_t* p_dev, const char* dev_name, uint ev.u.create.product = product_id; ev.u.create.version = version; ev.u.create.country = ctry_code; if (!com::android::bluetooth::flags::hid_report_queuing()) { result = uhid_write(p_dev->uhid.fd, &ev); log::warn("wrote descriptor to fd = {}, dscp_len = {}, result = {}", p_dev->uhid.fd, dscp_len, Loading @@ -620,6 +902,26 @@ void bta_hh_co_send_hid_info(btif_hh_device_t* p_dev, const char* dev_name, uint close(p_dev->uhid.fd); p_dev->uhid.fd = -1; } return; } to_uhid.type = BTA_HH_UHID_INBOUND_DSCP_EVT; ev.u.create.rd_data = (uint8_t*)osi_malloc(ev.u.create.rd_size); memcpy(ev.u.create.rd_data, p_dscp, ev.u.create.rd_size); if (!to_uhid_thread(p_dev->internal_send_fd, &to_uhid)) { log::warn("Error: failed to send DSCP"); if (p_dev->internal_send_fd >= 0) { // Detach the uhid thread. It will exit by itself upon receiving hangup. pthread_detach(p_dev->hh_poll_thread_id); p_dev->hh_poll_thread_id = -1; close(p_dev->internal_send_fd); p_dev->internal_send_fd = -1; } } return; } /******************************************************************************* Loading @@ -642,6 +944,16 @@ void bta_hh_co_set_rpt_rsp(uint8_t dev_handle, uint8_t status) { return; } if (com::android::bluetooth::flags::hid_report_queuing()) { tBTA_HH_TO_UHID_EVT to_uhid = {}; to_uhid.type = BTA_HH_UHID_INBOUND_SET_REPORT_EVT; to_uhid.uhid.type = UHID_SET_REPORT_REPLY; to_uhid.uhid.u.set_report_reply.err = status; to_uhid_thread(p_dev->internal_send_fd, &to_uhid); return; } if (!p_dev->uhid.set_rpt_id_queue) { log::warn("Missing UHID_SET_REPORT id queue"); return; Loading Loading @@ -700,6 +1012,23 @@ void bta_hh_co_get_rpt_rsp(uint8_t dev_handle, uint8_t status, const uint8_t* p_ return; } if (len == 0 || len > UHID_DATA_MAX) { log::warn("Invalid report size = {}", len); return; } if (com::android::bluetooth::flags::hid_report_queuing()) { tBTA_HH_TO_UHID_EVT to_uhid = {}; to_uhid.type = BTA_HH_UHID_INBOUND_GET_REPORT_EVT; to_uhid.uhid.type = UHID_FEATURE_ANSWER; to_uhid.uhid.u.feature_answer.err = status; to_uhid.uhid.u.feature_answer.size = len; memcpy(to_uhid.uhid.u.feature_answer.data, p_rpt, len); to_uhid_thread(p_dev->internal_send_fd, &to_uhid); return; } if (!p_dev->uhid.get_rpt_id_queue) { log::warn("Missing UHID_GET_REPORT id queue"); return; Loading @@ -718,11 +1047,6 @@ void bta_hh_co_get_rpt_rsp(uint8_t dev_handle, uint8_t status, const uint8_t* p_ return; } if (len == 0 || len > UHID_DATA_MAX) { log::warn("Invalid report size = {}", len); return; } struct uhid_event ev = { .type = UHID_FEATURE_ANSWER, .u = Loading
system/btif/include/btif_hh.h +9 −5 Original line number Diff line number Diff line Loading @@ -84,18 +84,20 @@ inline std::string btif_hh_status_text(const BTIF_HH_STATUS& status) { } } /* Supposedly is exclusive to uhid thread, but now is still accessed by btif. */ /* TODO: remove btif_hh_uhid_t from btif_hh_device_t. */ /* Uhid thread has exclusive access to this block. */ typedef struct { int fd; int fd; // for interfacing with uhid int internal_recv_fd; // for receiving internal events in uhid thread uint8_t dev_handle; tAclLinkSpec link_spec; uint8_t hh_keep_polling; uint8_t hh_keep_polling; // Deprecated with the aflags hid_report_queuing. // TODO: remove after launching the aflag. bool ready_for_data; fixed_queue_t* get_rpt_id_queue; #if ENABLE_UHID_SET_REPORT fixed_queue_t* set_rpt_id_queue; #endif // ENABLE_UHID_SET_REPORT fixed_queue_t* input_queue; // to store the inputs before uhid is ready. } btif_hh_uhid_t; /* Control block to maintain properties of devices */ Loading @@ -106,10 +108,12 @@ typedef struct { tBTA_HH_ATTR_MASK attr_mask; uint8_t sub_class; uint8_t app_id; int internal_send_fd; // for sending internal events from btif pthread_t hh_poll_thread_id; alarm_t* vup_timer; bool local_vup; // Indicated locally initiated VUP btif_hh_uhid_t uhid; btif_hh_uhid_t uhid; // Deprecated with the aflags hid_report_queuing. // TODO: remove after launching the aflag. } btif_hh_device_t; /* Control block to maintain properties of devices */ Loading
system/btif/src/btif_hh.cc +25 −11 Original line number Diff line number Diff line Loading @@ -295,7 +295,9 @@ static void sync_lockstate_on_connect(btif_hh_device_t* p_dev) { if (keylockstates) { log::verbose("Sending hid report to kernel indicating lock key state 0x{:x}", keylockstates); usleep(200000); toggle_os_keylockstates(p_dev->uhid.fd, keylockstates); int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); toggle_os_keylockstates(fd, keylockstates); } else { log::verbose("NOT sending hid report to kernel indicating lock key state 0x{:x}", keylockstates); Loading Loading @@ -557,8 +559,11 @@ static void hh_open_handler(tBTA_HH_CONN& conn) { log::info("Found device, getting dscp info for handle {}", conn.handle); if (!com::android::bluetooth::flags::hid_report_queuing()) { // link_spec and status is to be set in bta_hh_co_open instead. p_dev->link_spec = conn.link_spec; p_dev->dev_status = BTHH_CONN_STATE_CONNECTED; } hh_connect_complete(conn.handle, conn.link_spec, BTIF_HH_DEV_CONNECTED); // Send set_idle if the peer_device is a keyboard if (check_cod_hid_major(conn.link_spec.addrt.bda, COD_HID_KEYBOARD) || Loading Loading @@ -686,9 +691,11 @@ void btif_hh_remove_device(const tAclLinkSpec& link_spec) { bta_hh_co_close(p_dev); p_dev->dev_status = BTHH_CONN_STATE_UNKNOWN; p_dev->dev_handle = BTA_HH_INVALID_HANDLE; if (!com::android::bluetooth::flags::hid_report_queuing()) { p_dev->uhid.ready_for_data = false; } } } bool btif_hh_copy_hid_info(tBTA_HH_DEV_DSCP_INFO* dest, tBTA_HH_DEV_DSCP_INFO* src) { memset(dest, 0, sizeof(tBTA_HH_DEV_DSCP_INFO)); Loading Loading @@ -1000,8 +1007,10 @@ static void btif_hh_upstreams_evt(uint16_t event, char* p_param) { p_data->dev_status.handle); p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle); if (p_dev != NULL) { int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); BTHH_STATE_UPDATE(p_dev->link_spec, BTHH_CONN_STATE_DISCONNECTING); log::verbose("uhid fd={} local_vup={}", p_dev->uhid.fd, p_dev->local_vup); log::verbose("uhid fd={} local_vup={}", fd, p_dev->local_vup); btif_hh_stop_vup_timer(p_dev->link_spec); /* If this is a locally initiated VUP, remove the bond as ACL got * disconnected while VUP being processed. Loading Loading @@ -1131,7 +1140,9 @@ static void btif_hh_upstreams_evt(uint16_t event, char* p_param) { return; } if (p_dev->uhid.fd < 0) { int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); if (fd < 0) { log::error("BTA_HH_GET_DSCP_EVT: failed to find the uhid driver..."); return; } Loading Loading @@ -2042,8 +2053,10 @@ static void cleanup(void) { } for (i = 0; i < BTIF_HH_MAX_HID; i++) { p_dev = &btif_hh_cb.devices[i]; if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN && p_dev->uhid.fd >= 0) { log::verbose("Closing uhid fd = {}", p_dev->uhid.fd); int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN && fd >= 0) { log::verbose("Closing uhid fd = {}", fd); bta_hh_co_close(p_dev); } } Loading Loading @@ -2126,10 +2139,11 @@ void DumpsysHid(int fd) { for (unsigned i = 0; i < BTIF_HH_MAX_HID; i++) { const btif_hh_device_t* p_dev = &btif_hh_cb.devices[i]; if (p_dev->link_spec.addrt.bda != RawAddress::kEmpty) { LOG_DUMPSYS(fd, " %u: addr:%s fd:%d state:%s ready:%s thread_id:%d handle:%d", i, p_dev->link_spec.ToRedactedStringForLogging().c_str(), p_dev->uhid.fd, int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd : p_dev->uhid.fd); LOG_DUMPSYS(fd, " %u: addr:%s fd:%d state:%s thread_id:%d handle:%d", i, p_dev->link_spec.ToRedactedStringForLogging().c_str(), fd, bthh_connection_state_text(p_dev->dev_status).c_str(), (p_dev->uhid.ready_for_data) ? ("T") : ("F"), static_cast<int>(p_dev->hh_poll_thread_id), p_dev->dev_handle); } } Loading