/****************************************************************************** * * Copyright 2005-2012 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /****************************************************************************** * * This file contains the HID host main functions and state machine. * ******************************************************************************/ #define LOG_TAG "bt_bta_hh" #include #include #include "bta/hh/bta_hh_int.h" #include "bta_hh_api.h" #include "hiddefs.h" #include "main/shim/dumpsys.h" #include "osi/include/allocator.h" #include "stack/include/bt_hdr.h" // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" using namespace bluetooth; /***************************************************************************** * Global data ****************************************************************************/ tBTA_HH_CB bta_hh_cb; /***************************************************************************** * Static functions ****************************************************************************/ /******************************************************************************* * * Function bta_hh_evt_code * * Description * * Returns void * ******************************************************************************/ static const char* bta_hh_evt_code(tBTA_HH_INT_EVT evt_code) { switch (evt_code) { case BTA_HH_API_OPEN_EVT: return "BTA_HH_API_OPEN_EVT"; case BTA_HH_API_CLOSE_EVT: return "BTA_HH_API_CLOSE_EVT"; case BTA_HH_INT_OPEN_EVT: return "BTA_HH_INT_OPEN_EVT"; case BTA_HH_INT_CLOSE_EVT: return "BTA_HH_INT_CLOSE_EVT"; case BTA_HH_INT_HANDSK_EVT: return "BTA_HH_INT_HANDSK_EVT"; case BTA_HH_INT_DATA_EVT: return "BTA_HH_INT_DATA_EVT"; case BTA_HH_INT_CTRL_DATA: return "BTA_HH_INT_CTRL_DATA"; case BTA_HH_API_WRITE_DEV_EVT: return "BTA_HH_API_WRITE_DEV_EVT"; case BTA_HH_SDP_CMPL_EVT: return "BTA_HH_SDP_CMPL_EVT"; case BTA_HH_API_MAINT_DEV_EVT: return "BTA_HH_API_MAINT_DEV_EVT"; case BTA_HH_API_GET_DSCP_EVT: return "BTA_HH_API_GET_DSCP_EVT"; case BTA_HH_OPEN_CMPL_EVT: return "BTA_HH_OPEN_CMPL_EVT"; case BTA_HH_GATT_CLOSE_EVT: return "BTA_HH_GATT_CLOSE_EVT"; case BTA_HH_GATT_OPEN_EVT: return "BTA_HH_GATT_OPEN_EVT"; case BTA_HH_START_ENC_EVT: return "BTA_HH_START_ENC_EVT"; case BTA_HH_ENC_CMPL_EVT: return "BTA_HH_ENC_CMPL_EVT"; default: return "unknown HID Host event code"; } } /******************************************************************************* * * Function bta_hh_state_code * * Description get string representation of HID host state code. * * Returns void * ******************************************************************************/ static const char* bta_hh_state_code(tBTA_HH_STATE state_code) { switch (state_code) { case BTA_HH_NULL_ST: return "BTA_HH_NULL_ST"; case BTA_HH_IDLE_ST: return "BTA_HH_IDLE_ST"; case BTA_HH_W4_CONN_ST: return "BTA_HH_W4_CONN_ST"; case BTA_HH_CONN_ST: return "BTA_HH_CONN_ST"; case BTA_HH_W4_SEC: return "BTA_HH_W4_SEC"; default: return "unknown HID Host state"; } } /* Finds the related control block, if any */ static tBTA_HH_DEV_CB* bta_hh_find_cb_by_event(const BT_HDR_RIGID* p_msg) { tBTA_HH_DEV_CB* p_cb = nullptr; if (p_msg->event == BTA_HH_API_OPEN_EVT) { // Connection requested, find or allocate the control block p_cb = bta_hh_get_cb(((tBTA_HH_API_CONN*)p_msg)->link_spec); } else if (p_msg->event == BTA_HH_API_MAINT_DEV_EVT) { if (((tBTA_HH_MAINT_DEV*)p_msg)->sub_event == BTA_HH_ADD_DEV_EVT) { // Device is being added, find or allocate the control block p_cb = bta_hh_get_cb(((tBTA_HH_MAINT_DEV*)p_msg)->link_spec); } else /* else remove device by handle */ { p_cb = bta_hh_find_cb_by_handle((uint8_t)p_msg->layer_specific); /* If BT disable is done while the HID device is connected and * Link_Key uses unauthenticated combination * then we can get into a situation where remove_bonding is called * with the index set to 0 (without getting * cleaned up). Only when VIRTUAL_UNPLUG is called do we cleanup the * index and make it MAX_KNOWN. * So if REMOVE_DEVICE is called and in_use is false then we should * treat this as a NULL p_cb. Hence we * force the index to be IDX_INVALID */ if (p_cb != nullptr && !p_cb->in_use) { log::warn("Control block getting removed, device: {}, index: {}, handle: {}", p_cb->link_spec, p_cb->index, p_cb->hid_handle); p_cb = nullptr; } } } else if (p_msg->event == BTA_HH_INT_OPEN_EVT) { p_cb = bta_hh_get_cb(((tBTA_HH_CBACK_DATA*)p_msg)->link_spec); } else { p_cb = bta_hh_find_cb_by_handle((uint8_t)p_msg->layer_specific); } return p_cb; } /* Handles events related to connection control blocks */ void bta_hh_sm_execute(tBTA_HH_DEV_CB* p_cb, tBTA_HH_INT_EVT event, const tBTA_HH_DATA* p_data) { tBTA_HH_STATE in_state = p_cb->state; if (p_cb->state == BTA_HH_NULL_ST || p_cb->state >= BTA_HH_INVALID_ST) { log::error("Invalid state State:{}, Event:{} for {}", bta_hh_state_code(in_state), bta_hh_evt_code(event), p_cb->link_spec); return; } bool unexpected_event = false; log::verbose("State {}, Event {} for {}", bta_hh_state_code(in_state), bta_hh_evt_code(event), p_cb->link_spec); switch (in_state) { case BTA_HH_IDLE_ST: switch (event) { case BTA_HH_API_OPEN_EVT: p_cb->state = BTA_HH_W4_CONN_ST; bta_hh_connect(p_cb, p_data); break; case BTA_HH_INT_OPEN_EVT: p_cb->state = BTA_HH_W4_CONN_ST; bta_hh_open_act(p_cb, p_data); break; case BTA_HH_INT_CLOSE_EVT: bta_hh_open_failure(p_cb, p_data); break; case BTA_HH_API_MAINT_DEV_EVT: bta_hh_maint_dev_act(p_cb, p_data); break; case BTA_HH_OPEN_CMPL_EVT: p_cb->state = BTA_HH_CONN_ST; bta_hh_open_cmpl_act(p_cb, p_data); break; case BTA_HH_GATT_OPEN_EVT: p_cb->state = BTA_HH_W4_CONN_ST; bta_hh_gatt_open(p_cb, p_data); break; default: unexpected_event = true; break; } break; case BTA_HH_W4_CONN_ST: switch (event) { case BTA_HH_API_CLOSE_EVT: p_cb->state = BTA_HH_IDLE_ST; break; case BTA_HH_INT_OPEN_EVT: bta_hh_open_act(p_cb, p_data); break; case BTA_HH_INT_CLOSE_EVT: p_cb->state = BTA_HH_IDLE_ST; bta_hh_open_failure(p_cb, p_data); break; case BTA_HH_SDP_CMPL_EVT: bta_hh_sdp_cmpl(p_cb, p_data); break; case BTA_HH_API_WRITE_DEV_EVT: bta_hh_write_dev_act(p_cb, p_data); break; case BTA_HH_API_MAINT_DEV_EVT: p_cb->state = BTA_HH_IDLE_ST; bta_hh_maint_dev_act(p_cb, p_data); break; case BTA_HH_OPEN_CMPL_EVT: p_cb->state = BTA_HH_CONN_ST; bta_hh_open_cmpl_act(p_cb, p_data); break; case BTA_HH_GATT_CLOSE_EVT: p_cb->state = BTA_HH_IDLE_ST; bta_hh_le_open_fail(p_cb, p_data); break; case BTA_HH_GATT_OPEN_EVT: bta_hh_gatt_open(p_cb, p_data); break; case BTA_HH_START_ENC_EVT: p_cb->state = BTA_HH_W4_SEC; bta_hh_start_security(p_cb, p_data); break; default: unexpected_event = true; break; } break; case BTA_HH_CONN_ST: switch (event) { case BTA_HH_API_CLOSE_EVT: bta_hh_api_disc_act(p_cb, p_data); break; case BTA_HH_INT_OPEN_EVT: bta_hh_open_act(p_cb, p_data); break; case BTA_HH_INT_CLOSE_EVT: p_cb->state = BTA_HH_IDLE_ST; bta_hh_close_act(p_cb, p_data); break; case BTA_HH_INT_DATA_EVT: bta_hh_data_act(p_cb, p_data); break; case BTA_HH_INT_CTRL_DATA: bta_hh_ctrl_dat_act(p_cb, p_data); break; case BTA_HH_INT_HANDSK_EVT: bta_hh_handsk_act(p_cb, p_data); break; case BTA_HH_API_WRITE_DEV_EVT: bta_hh_write_dev_act(p_cb, p_data); break; case BTA_HH_API_GET_DSCP_EVT: bta_hh_get_dscp_act(p_cb, p_data); break; case BTA_HH_API_MAINT_DEV_EVT: bta_hh_maint_dev_act(p_cb, p_data); break; case BTA_HH_GATT_CLOSE_EVT: p_cb->state = BTA_HH_IDLE_ST; bta_hh_gatt_close(p_cb, p_data); break; default: unexpected_event = true; break; } break; case BTA_HH_W4_SEC: switch (event) { case BTA_HH_API_CLOSE_EVT: bta_hh_api_disc_act(p_cb, p_data); break; case BTA_HH_INT_CLOSE_EVT: p_cb->state = BTA_HH_IDLE_ST; bta_hh_open_failure(p_cb, p_data); break; case BTA_HH_API_MAINT_DEV_EVT: bta_hh_maint_dev_act(p_cb, p_data); break; case BTA_HH_GATT_CLOSE_EVT: p_cb->state = BTA_HH_IDLE_ST; bta_hh_le_open_fail(p_cb, p_data); break; case BTA_HH_ENC_CMPL_EVT: p_cb->state = BTA_HH_W4_CONN_ST; bta_hh_security_cmpl(p_cb, p_data); break; case BTA_HH_GATT_ENC_CMPL_EVT: bta_hh_le_notify_enc_cmpl(p_cb, p_data); break; default: unexpected_event = true; break; } break; } if (unexpected_event) { log::warn("Unexpected event event {} in state {} for {}", bta_hh_evt_code(event), bta_hh_state_code(in_state), p_cb->link_spec); } else if (in_state != p_cb->state) { log::debug("State Change: [{}] -> [{}] after Event [{}]", bta_hh_state_code(in_state), bta_hh_state_code(p_cb->state), bta_hh_evt_code(event)); } } /******************************************************************************* * * Function bta_hh_hdl_failure * * Description Handler for state machine failures * * Returns void * ******************************************************************************/ void bta_hh_hdl_failure(tBTA_HH_INT_EVT event, const tBTA_HH_DATA* p_data) { if (bta_hh_cb.p_cback == nullptr) { log::error("No callback handler"); return; } log::verbose("Event:{}", bta_hh_evt_code(event)); tBTA_HH cback_data = {}; tBTA_HH_EVT cback_event = BTA_HH_EMPTY_EVT; switch (event) { /* no control block available for new connection */ case BTA_HH_API_OPEN_EVT: cback_event = BTA_HH_OPEN_EVT; /* build cback data */ cback_data.conn.link_spec = ((tBTA_HH_API_CONN*)p_data)->link_spec; cback_data.conn.status = BTA_HH_ERR_DB_FULL; cback_data.conn.handle = BTA_HH_INVALID_HANDLE; break; /* DB full, BTA_HhAddDev */ case BTA_HH_API_MAINT_DEV_EVT: cback_event = p_data->api_maintdev.sub_event; if (p_data->api_maintdev.sub_event == BTA_HH_ADD_DEV_EVT) { cback_data.dev_info.link_spec = p_data->api_maintdev.link_spec; cback_data.dev_info.status = BTA_HH_ERR_DB_FULL; cback_data.dev_info.handle = BTA_HH_INVALID_HANDLE; } else { cback_data.dev_info.status = BTA_HH_ERR_HDL; cback_data.dev_info.handle = (uint8_t)p_data->api_maintdev.hdr.layer_specific; } break; case BTA_HH_API_WRITE_DEV_EVT: cback_event = (p_data->api_sndcmd.t_type - HID_TRANS_GET_REPORT) + BTA_HH_GET_RPT_EVT; osi_free_and_reset((void**)&p_data->api_sndcmd.p_data); if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL || p_data->api_sndcmd.t_type == HID_TRANS_SET_REPORT || p_data->api_sndcmd.t_type == HID_TRANS_SET_IDLE) { cback_data.dev_status.status = BTA_HH_ERR_HDL; cback_data.dev_status.handle = (uint8_t)p_data->api_sndcmd.hdr.layer_specific; } else if (p_data->api_sndcmd.t_type != HID_TRANS_DATA && p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) { cback_data.hs_data.handle = (uint8_t)p_data->api_sndcmd.hdr.layer_specific; cback_data.hs_data.status = BTA_HH_ERR_HDL; /* hs_data.rsp_data will be all zero, which is not valid value */ } else if (p_data->api_sndcmd.t_type == HID_TRANS_CONTROL && p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) { cback_data.status = BTA_HH_ERR_HDL; cback_event = BTA_HH_VC_UNPLUG_EVT; } else { cback_event = 0; } break; case BTA_HH_API_CLOSE_EVT: cback_event = BTA_HH_CLOSE_EVT; cback_data.dev_status.status = BTA_HH_ERR_HDL; cback_data.dev_status.handle = (uint8_t)p_data->api_sndcmd.hdr.layer_specific; break; default: /* Likely an invalid handle, call bad API event */ log::error("wrong device handle:{} in event:{}", p_data->hdr.layer_specific, bta_hh_evt_code(event)); /* Free the callback buffer now */ if (p_data != nullptr) { osi_free_and_reset((void**)&p_data->hid_cback.p_data); } break; } if (cback_event) { (*bta_hh_cb.p_cback)(cback_event, &cback_data); } } /******************************************************************************* * * Function bta_hh_hdl_event * * Description HID host main event handling function. * * * Returns void * ******************************************************************************/ bool bta_hh_hdl_event(const BT_HDR_RIGID* p_msg) { tBTA_HH_DEV_CB* p_cb = bta_hh_find_cb_by_event(p_msg); tBTA_HH_INT_EVT event = static_cast(p_msg->event); if (p_cb != nullptr) { bta_hh_sm_execute(p_cb, event, (tBTA_HH_DATA*)p_msg); } else { bta_hh_hdl_failure(event, (tBTA_HH_DATA*)p_msg); } return true; } #define DUMPSYS_TAG "shim::legacy::hid" void bta_hh_dump(int fd) { for (auto dev : bta_hh_cb.kdev) { if (dev.in_use) { LOG_DUMPSYS(fd, "[%d] Device:%s, handle:%d, state:%s, sub class:%d, ", dev.index, dev.link_spec.ToRedactedStringForLogging().c_str(), dev.hid_handle, bta_hh_state_code(dev.state), dev.sub_class); } } } #undef DUMPSYS_TAG