#include "btif/include/btif_debug_btsnoop.h"
#include "hci/include/btsnoop_mem.h"
#include "internal_include/bt_target.h"
+#include "osi/include/properties.h"
#include "osi/include/ringbuffer.h"
#define REDUCE_HCI_TYPE_TO_SIGNIFICANT_BITS(type) ((type) >> 8)
static std::mutex buffer_mutex;
static ringbuffer_t* buffer = NULL;
static uint64_t last_timestamp_ms = 0;
+static bool qualcomm_debug_log_enable = false;
static size_t btsnoop_calculate_packet_length(uint16_t type,
const uint8_t* data,
static const size_t L2CAP_CID_OFFSET = (HCI_ACL_HEADER_SIZE + 2);
static const uint16_t L2CAP_SIGNALING_CID = 0x0001;
+ static const size_t HCI_ACL_HANDLE_OFFSET = 0;
+ static const uint16_t QUALCOMM_DEBUG_LOG_HANDLE = 0xedc;
+
// Maximum amount of ACL data to log.
// Enough for an RFCOMM frame up to the frame check;
// not enough for a HID report or audio data.
if (length > len_hci_acl) {
uint16_t l2cap_cid =
data[L2CAP_CID_OFFSET] | (data[L2CAP_CID_OFFSET + 1] << 8);
+ uint16_t hci_acl_packet_handle = data[HCI_ACL_HANDLE_OFFSET] |
+ (data[HCI_ACL_HANDLE_OFFSET + 1] << 8);
+ hci_acl_packet_handle &= 0x0FFF;
+
if (l2cap_cid == L2CAP_SIGNALING_CID) {
// For the signaling CID, take the full packet.
// That way, the PSM setup is captured, allowing decoding of PSMs down
// the road.
return length;
+ } else if (qualcomm_debug_log_enable &&
+ hci_acl_packet_handle == QUALCOMM_DEBUG_LOG_HANDLE) {
+ // For the enhanced controller debug log, take the full packet.
+ return length;
} else {
// Otherwise, return as much as we reasonably can
len_hci_acl = MAX_HCI_ACL_LEN;
void btif_debug_btsnoop_init(void) {
if (buffer == NULL) buffer = ringbuffer_init(BTSNOOP_MEM_BUFFER_SIZE);
btsnoop_mem_set_callback(btsnoop_cb);
+
+ char value[PROPERTY_VALUE_MAX] = {0};
+ int ret = osi_property_get("ro.soc.manufacturer", value, "");
+ if (ret > 0 && strncmp(value, "Qualcomm", ret) == 0) {
+ qualcomm_debug_log_enable = true;
+ }
}
#ifndef OS_ANDROID
* Description This function converts an ASCII string into HEX
*
* Returns the number of hex bytes filled.
-*/
+ */
int ascii_2_hex(const char* p_ascii, int len, uint8_t* p_hex) {
int x;
uint8_t c;
CASE_RETURN_STR(BTA_DM_BLE_PASSKEY_NOTIF_EVT)
CASE_RETURN_STR(BTA_DM_BLE_PASSKEY_REQ_EVT)
CASE_RETURN_STR(BTA_DM_BLE_OOB_REQ_EVT)
+ CASE_RETURN_STR(BTA_DM_BLE_SC_OOB_REQ_EVT)
CASE_RETURN_STR(BTA_DM_BLE_LOCAL_IR_EVT)
CASE_RETURN_STR(BTA_DM_BLE_LOCAL_ER_EVT)
CASE_RETURN_STR(BTA_DM_BLE_AUTH_CMPL_EVT)
if (p_msg->vendor_len == 0) return AVRC_STS_NO_ERROR;
if (p_msg->p_vendor_data == NULL) return AVRC_STS_INTERNAL_ERR;
- if (p_msg->vendor_len < 4) {
- android_errorWriteLog(0x534e4554, "168712382");
- AVRC_TRACE_WARNING("%s: message length %d too short: must be at least 4",
- __func__, p_msg->vendor_len);
- return AVRC_STS_INTERNAL_ERR;
- }
-
p = p_msg->p_vendor_data;
p_result->pdu = *p++;
AVRC_TRACE_DEBUG("%s pdu:0x%x", __func__, p_result->pdu);
case BTM_BLE_PF_ADDR_FILTER: {
tBLE_BD_ADDR target_addr;
target_addr.bda = cmd.address;
- target_addr.type = cmd.addr_type;
+ target_addr.type = (cmd.addr_type & (~BLE_ADDR_TYPE_ID_BIT));
BTM_LE_PF_addr_filter(action, filt_index, target_addr,
base::DoNothing());
return;
}
// Allocate a new "temporary" device record
+
btm_sec_alloc_dev(cmd.address);
remove_me_later_map.emplace(filt_index, cmd.address);
// Set the IRK
tBTM_LE_PID_KEYS pid_keys;
pid_keys.irk = cmd.irk;
- pid_keys.identity_addr_type = cmd.addr_type;
+ pid_keys.identity_addr_type =
+ (cmd.addr_type & (~BLE_ADDR_TYPE_ID_BIT));
pid_keys.identity_addr = cmd.address;
// Add it to the union to pass to SecAddBleKey
tBTM_LE_KEY_VALUE le_key;
/* clear service data filter */
BTM_LE_PF_srvc_data_pattern(BTM_BLE_SCAN_COND_CLEAR, filt_index, {}, {},
fDoNothing);
-
- // If we have an entry, lets remove the device if it isn't bonded
- auto entry = remove_me_later_map.find(filt_index);
- if (entry != remove_me_later_map.end()) {
- auto entry = remove_me_later_map.find(filt_index);
- if (!btm_sec_is_a_bonded_dev(entry->second)) {
- BTM_SecDeleteDevice(entry->second);
- }
- }
}
uint8_t len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_PF_FEAT_SEL_LEN;
(uint8_t)(BTM_BLE_ADV_FILT_META_HDR_LENGTH),
base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_FEAT_SEL, cb));
- auto entry = remove_me_later_map.find(filt_index);
- if (entry != remove_me_later_map.end()) {
- LOG_WARN("Replacing existing filter index entry with new address");
- // If device is not bonded, then try removing the device
- // If the device doesn't get removed then it is currently connected
- // (may be pairing?) If we do delete the device we want to erase the
- // filter index so we can replace it If the device is bonded, we
- // want to erase the filter index so we don't delete it in the later
- // BTM_LE_PF_clear call.
- if (!btm_sec_is_a_bonded_dev(entry->second)) {
- if (!BTM_SecDeleteDevice(entry->second)) {
- LOG_WARN("Unable to remove device, still connected.");
- return;
- }
- }
- remove_me_later_map.erase(filt_index);
- }
-
} else if (BTM_BLE_SCAN_COND_CLEAR == action) {
/* Deallocate all filters here */
btm_ble_dealloc_addr_filter_counter(NULL, BTM_BLE_PF_TYPE_ALL);
static void l2c_link_send_to_lower_br_edr(tL2C_LCB* p_lcb, BT_HDR* p_buf) {
const uint16_t acl_packet_size_classic =
controller_get_interface()->get_acl_packet_size_classic();
+ const uint16_t acl_data_size_classic =
+ controller_get_interface()->get_acl_data_size_classic();
const uint16_t link_xmit_quota = p_lcb->link_xmit_quota;
const bool is_bdr_and_fits_in_buffer =
bluetooth::shim::is_gd_acl_enabled()
l2cb.controller_xmit_window--;
} else {
uint16_t num_segs =
- (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_packet_size_classic - 1) /
- acl_packet_size_classic;
+ (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size_classic - 1) /
+ acl_data_size_classic;
/* If doing round-robin, then only 1 segment each time */
if (p_lcb->link_xmit_quota == 0) {
static void l2c_link_send_to_lower_ble(tL2C_LCB* p_lcb, BT_HDR* p_buf) {
const uint16_t acl_packet_size_ble =
controller_get_interface()->get_acl_packet_size_ble();
+ const uint16_t acl_data_size_ble =
+ controller_get_interface()->get_acl_data_size_ble();
const uint16_t link_xmit_quota = p_lcb->link_xmit_quota;
const bool is_ble_and_fits_in_buffer = (p_buf->len <= acl_packet_size_ble);
l2cb.controller_le_xmit_window--;
} else {
uint16_t num_segs =
- (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_packet_size_ble - 1) /
- acl_packet_size_ble;
+ (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size_ble - 1) /
+ acl_data_size_ble;
/* If doing round-robin, then only 1 segment each time */
if (p_lcb->link_xmit_quota == 0) {
* Description process IO response for a peripheral device.
******************************************************************************/
void smp_process_io_response(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
-
SMP_TRACE_DEBUG("%s", __func__);
if (p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) {
/* pairing started by local (peripheral) Security Request */
// If we are doing SMP_MODEL_SEC_CONN_OOB we don't need to request OOB data
// locally if loc_oob_flag == 0x00 b/c there is no OOB data to give. In the
- // event the loc_oob_flag is another value, we should request the OOB data
- // locally. Which seems to cause it to make a TK REQUEST which is used for
- // the legacy flow which requires both sides to have OOB data.
- if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB &&
- p_cb->loc_oob_flag != 0x00) {
- if (smp_request_oob_data(p_cb)) return;
+ // event the loc_oob_flag is present value, we should request the OOB data
+ // locally; otherwise fail.
+ // If we are the initiator the OOB data has already been stored and will be
+ // collected in the statemachine later.
+ //
+ // loc_oob_flag could be one of the following tSMP_OOB_FLAG enum values:
+ // SMP_OOB_NONE = 0
+ // SMP_OOB_PRESENT = 1
+ // SMP_OOB_UNKNOWN = 2
+ //
+ // The only time Android cares about needing to provide the peer oob data
+ // here would be in the advertiser situation or role. If the
+ // device is doing the connecting it will not need to get the data again as
+ // it was already provided in the initiation call.
+ //
+ // loc_oob_flag should only equal SMP_OOB_PRESENT when PEER DATA exists and
+ // device is the advertiser as opposed to being the connector.
+ if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) {
+ switch (p_cb->loc_oob_flag) {
+ case SMP_OOB_NONE:
+ LOG_INFO("SMP_MODEL_SEC_CONN_OOB with SMP_OOB_NONE");
+ smp_send_pair_rsp(p_cb, NULL);
+ break;
+ case SMP_OOB_PRESENT:
+ LOG_INFO("SMP_MODEL_SEC_CONN_OOB with SMP_OOB_PRESENT");
+ if (smp_request_oob_data(p_cb)) return;
+ break;
+ case SMP_OOB_UNKNOWN:
+ LOG_WARN("SMP_MODEL_SEC_CONN_OOB with SMP_OOB_UNKNOWN");
+ tSMP_INT_DATA smp_int_data;
+ smp_int_data.status = SMP_PAIR_AUTH_FAIL;
+ smp_send_pair_fail(p_cb, &smp_int_data);
+ return;
+ }
}
// PTS Testing failure modes
if (!p_oob->loc_oob_data.present) data_missing = true;
break;
case SMP_OOB_BOTH:
+ // Check for previous local OOB data in cache
+ // This would be in the case data was generated BEFORE pairing was
+ // attempted and this instance is the connector or pairing initiator.
+ // [NOTICE]: Overridding data present here if the data exists so state
+ // machine asks for it later
+ p_oob->loc_oob_data.present = smp_has_local_oob_data();
if (!p_oob->loc_oob_data.present || !p_oob->peer_oob_data.present)
data_missing = true;
break;
* cleaned up */
extern void smp_save_local_oob_data(tSMP_CB* p_cb);
extern void smp_clear_local_oob_data();
-
+extern bool smp_has_local_oob_data();
#endif /* SMP_INT_H */
return memcmp(data, &empty_data, sizeof(tSMP_LOC_OOB_DATA)) == 0;
}
+bool smp_has_local_oob_data() { return !is_empty(&saved_local_oob_data); }
+
void smp_debug_print_nbyte_little_endian(uint8_t* p, const char* key_name,
- uint8_t len) {
-}
+ uint8_t len) {}
inline void smp_debug_print_nbyte_little_endian(const Octet16& p,
const char* key_name,
}
void smp_debug_print_nbyte_big_endian(uint8_t* p, const char* key_name,
- uint8_t len) {
-}
+ uint8_t len) {}
/** This function is called to process a passkey. */
void smp_proc_passkey(tSMP_CB* p_cb, BT_OCTET8 rand) {
case SMP_OOB_BOTH:
case SMP_OOB_LOCAL:
LOG_INFO("restore secret key");
+ // Only use the stored OOB data if we are in an oob association model
+ if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) {
+ LOG_INFO("OOB Association Model");
+ // Make sure our data isn't empty, otherwise we generate new and
+ // eventually pairing will fail Not much we can do about it at this
+ // point, just have to generate new data The data will be cleared after
+ // the advertiser times out, so if the advertiser times out we want the
+ // pairing to fail anyway.
+ if (!is_empty(&saved_local_oob_data)) {
+ LOG_INFO("Found OOB data, loading keys");
+ for (int i = 0; i < BT_OCTET32_LEN; i++) {
+ p_cb->private_key[i] = saved_local_oob_data.private_key_used[i];
+ p_cb->loc_publ_key.x[i] = saved_local_oob_data.publ_key_used.x[i];
+ p_cb->loc_publ_key.y[i] = saved_local_oob_data.publ_key_used.y[i];
+ }
+ p_cb->sc_oob_data.loc_oob_data = saved_local_oob_data;
+ p_cb->local_random = saved_local_oob_data.randomizer;
+ smp_process_private_key(p_cb);
+ return;
+ }
+ LOG_INFO("OOB Association Model with no saved data present");
+ }
+
memcpy(p_cb->private_key, p_cb->sc_oob_data.loc_oob_data.private_key_used,
BT_OCTET32_LEN);
smp_process_private_key(p_cb);
return;
}
-
/*******************************************************************************
*
* Function smp_calculate_local_dhkey_check
smp_sm_event(p_cb, SMP_SC_KEY_READY_EVT, &smp_int_data);
}
-
/*******************************************************************************
*
* Function smp_calculate_link_key_from_long_term_key
"model/devices/device.cc",
"model/devices/device_properties.cc",
"model/devices/h4_packetizer.cc",
+ "model/devices/h4_parser.cc",
"model/devices/hci_protocol.cc",
"model/devices/hci_socket_device.cc",
"model/devices/keyboard.cc",
],
srcs: [
"test/async_manager_unittest.cc",
+ "test/h4_parser_unittest.cc",
"test/security_manager_unittest.cc",
],
header_libs: [
"packets/link_layer_packets.h",
],
}
+
+// bt_vhci_forwarder in cuttlefish depends on this H4Packetizer implementation.
+cc_library_static {
+ name: "h4_packetizer_lib",
+ vendor: true,
+ defaults: [
+ "gd_defaults",
+ ],
+ srcs: [
+ "model/devices/h4_packetizer.cc",
+ "model/devices/h4_parser.cc",
+ "model/devices/hci_protocol.cc",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+ export_include_dirs: [
+ "include",
+ ".",
+ ],
+ generated_headers: [
+ "libbt_init_flags_bridge_header",
+ ],
+ include_dirs: [
+ "system/bt",
+ "system/bt/gd",
+ ],
+}
#include "h4_packetizer.h"
#include <cerrno>
-
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>
#include "os/log.h"
+#include "osi/include/osi.h"
namespace test_vendor_lib {
-size_t H4Packetizer::HciGetPacketLengthForType(PacketType type,
- const uint8_t* preamble) const {
- static const size_t
- packet_length_offset[static_cast<size_t>(PacketType::ISO) + 1] = {
- 0,
- H4Packetizer::COMMAND_LENGTH_OFFSET,
- H4Packetizer::ACL_LENGTH_OFFSET,
- H4Packetizer::SCO_LENGTH_OFFSET,
- H4Packetizer::EVENT_LENGTH_OFFSET,
- H4Packetizer::ISO_LENGTH_OFFSET,
- };
-
- size_t offset = packet_length_offset[static_cast<size_t>(type)];
- size_t size = preamble[offset];
- if (type == PacketType::ACL) {
- size |= ((size_t)preamble[offset + 1]) << 8u;
- }
- if (type == PacketType::ISO) {
- size |= ((size_t)preamble[offset + 1] & 0x0fu) << 8u;
- }
- return size;
-}
H4Packetizer::H4Packetizer(int fd, PacketReadCallback command_cb,
PacketReadCallback event_cb,
PacketReadCallback iso_cb,
ClientDisconnectCallback disconnect_cb)
: uart_fd_(fd),
- command_cb_(std::move(command_cb)),
- event_cb_(std::move(event_cb)),
- acl_cb_(std::move(acl_cb)),
- sco_cb_(std::move(sco_cb)),
- iso_cb_(std::move(iso_cb)),
+ h4_parser_(command_cb, event_cb, acl_cb, sco_cb, iso_cb),
disconnect_cb_(std::move(disconnect_cb)) {}
size_t H4Packetizer::Send(uint8_t type, const uint8_t* data, size_t length) {
struct iovec iov[] = {{&type, sizeof(type)}, {const_cast<uint8_t*>(data), length}};
ssize_t ret = 0;
do {
- ret = TEMP_FAILURE_RETRY(writev(uart_fd_, iov, sizeof(iov) / sizeof(iov[0])));
+ OSI_NO_INTR(ret = writev(uart_fd_, iov, sizeof(iov) / sizeof(iov[0])));
} while (-1 == ret && EAGAIN == errno);
if (ret == -1) {
return ret;
}
-void H4Packetizer::OnPacketReady() {
- switch (hci_packet_type_) {
- case PacketType::COMMAND:
- command_cb_(packet_);
- break;
- case PacketType::ACL:
- acl_cb_(packet_);
- break;
- case PacketType::SCO:
- sco_cb_(packet_);
- break;
- case PacketType::EVENT:
- event_cb_(packet_);
- break;
- case PacketType::ISO:
- iso_cb_(packet_);
- break;
- default:
- LOG_ALWAYS_FATAL("Unimplemented packet type %d",
- static_cast<int>(hci_packet_type_));
- }
- // Get ready for the next type byte.
- hci_packet_type_ = PacketType::UNKNOWN;
-}
void H4Packetizer::OnDataReady(int fd) {
if (disconnected_) return;
- ssize_t bytes_to_read = 0;
- uint8_t* buffer_pointer = nullptr;
-
- static const size_t preamble_size[static_cast<size_t>(PacketType::ISO) + 1] =
- {
- 0,
- H4Packetizer::COMMAND_PREAMBLE_SIZE,
- H4Packetizer::ACL_PREAMBLE_SIZE,
- H4Packetizer::SCO_PREAMBLE_SIZE,
- H4Packetizer::EVENT_PREAMBLE_SIZE,
- H4Packetizer::ISO_PREAMBLE_SIZE,
- };
- switch (state_) {
- case HCI_TYPE:
- bytes_to_read = 1;
- buffer_pointer = &packet_type_;
- break;
- case HCI_PREAMBLE:
- case HCI_PAYLOAD:
- bytes_to_read = packet_.size() - bytes_read_;
- buffer_pointer = packet_.data() + bytes_read_;
- break;
- }
+ ssize_t bytes_to_read = h4_parser_.BytesRequested();
+ std::vector<uint8_t> buffer(bytes_to_read);
- ssize_t bytes_read =
- TEMP_FAILURE_RETRY(read(fd, buffer_pointer, bytes_to_read));
+ ssize_t bytes_read;
+ OSI_NO_INTR(bytes_read = read(fd, buffer.data(), bytes_to_read));
if (bytes_read == 0) {
LOG_INFO("remote disconnected!");
disconnected_ = true;
disconnect_cb_();
return;
} else {
- LOG_ALWAYS_FATAL(
- "Read error in %s: %s",
- state_ == HCI_TYPE
- ? "HCI_TYPE"
- : state_ == HCI_PREAMBLE ? "HCI_PREAMBLE" : "HCI_PAYLOAD",
- strerror(errno));
+ LOG_ALWAYS_FATAL("Read error in %d: %s", h4_parser_.CurrentState(),
+ strerror(errno));
}
- } else if (bytes_read > bytes_to_read) {
- LOG_ALWAYS_FATAL("More bytes read (%u) than expected (%u)!",
- static_cast<int>(bytes_read),
- static_cast<int>(bytes_to_read));
- }
-
- switch (state_) {
- case HCI_TYPE:
- hci_packet_type_ = static_cast<PacketType>(packet_type_);
- if (hci_packet_type_ != PacketType::ACL &&
- hci_packet_type_ != PacketType::SCO &&
- hci_packet_type_ != PacketType::COMMAND &&
- hci_packet_type_ != PacketType::EVENT &&
- hci_packet_type_ != PacketType::ISO) {
- LOG_ALWAYS_FATAL("Unimplemented packet type %hhd", packet_type_);
- }
- state_ = HCI_PREAMBLE;
- bytes_read_ = 0;
- packet_.resize(preamble_size[static_cast<size_t>(hci_packet_type_)]);
- break;
- case HCI_PREAMBLE:
- bytes_read_ += bytes_read;
- if (bytes_read_ == packet_.size()) {
- size_t payload_size =
- HciGetPacketLengthForType(hci_packet_type_, packet_.data());
- if (payload_size == 0) {
- OnPacketReady();
- state_ = HCI_TYPE;
- } else {
- packet_.resize(packet_.size() + payload_size);
- state_ = HCI_PAYLOAD;
- }
- }
- break;
- case HCI_PAYLOAD:
- bytes_read_ += bytes_read;
- if (bytes_read_ == packet_.size()) {
- OnPacketReady();
- state_ = HCI_TYPE;
- }
- break;
}
+ h4_parser_.Consume(buffer.data(), bytes_read);
}
} // namespace test_vendor_lib
#include <functional>
#include <vector>
+#include "h4_parser.h"
#include "hci_protocol.h"
namespace test_vendor_lib {
-using HciPacketReadyCallback = std::function<void(void)>;
-using ClientDisconnectCallback = std::function<void()>;
-
-enum class PacketType : uint8_t {
- UNKNOWN = 0,
- COMMAND = 1,
- ACL = 2,
- SCO = 3,
- EVENT = 4,
- ISO = 5,
-};
-
+// A socket based H4Packetizer. Call OnDataReady whenever
+// data can be read from file descriptor fd.
+//
+// This is only supported on unix.
class H4Packetizer : public HciProtocol {
public:
H4Packetizer(int fd, PacketReadCallback command_cb,
size_t Send(uint8_t type, const uint8_t* data, size_t length) override;
- void OnPacketReady();
-
void OnDataReady(int fd);
- // 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
- static constexpr size_t COMMAND_PREAMBLE_SIZE = 3;
- static constexpr size_t COMMAND_LENGTH_OFFSET = 2;
-
- // 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
- static constexpr size_t ACL_PREAMBLE_SIZE = 4;
- static constexpr size_t ACL_LENGTH_OFFSET = 2;
-
- // 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
- static constexpr size_t SCO_PREAMBLE_SIZE = 3;
- static constexpr size_t SCO_LENGTH_OFFSET = 2;
-
- // 1 byte for event code, 1 byte for parameter length (Volume 2, Part
- // E, 5.4.4)
- static constexpr size_t EVENT_PREAMBLE_SIZE = 2;
- static constexpr size_t EVENT_LENGTH_OFFSET = 1;
-
- // 2 bytes for handle and flags, 12 bits for length (Volume 2, Part E, 5.4.5)
- static constexpr size_t ISO_PREAMBLE_SIZE = 4;
- static constexpr size_t ISO_LENGTH_OFFSET = 2;
-
private:
int uart_fd_;
-
- PacketReadCallback command_cb_;
- PacketReadCallback event_cb_;
- PacketReadCallback acl_cb_;
- PacketReadCallback sco_cb_;
- PacketReadCallback iso_cb_;
+ H4Parser h4_parser_;
ClientDisconnectCallback disconnect_cb_;
bool disconnected_{false};
-
- size_t HciGetPacketLengthForType(PacketType type,
- const uint8_t* preamble) const;
-
- PacketType hci_packet_type_{PacketType::UNKNOWN};
-
- enum State { HCI_TYPE, HCI_PREAMBLE, HCI_PAYLOAD };
- State state_{HCI_TYPE};
- uint8_t packet_type_{};
- std::vector<uint8_t> packet_;
- size_t bytes_read_{0};
};
} // namespace test_vendor_lib
--- /dev/null
+//
+// Copyright 20 The Android Open Source Project
+//
+// 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.
+//
+
+#include "model/devices/h4_parser.h" // for H4Parser, PacketType, H4Pars...
+
+#include <stddef.h> // for size_t
+#include <cstdint> // for uint8_t, int32_t
+#include <functional> // for function
+#include <utility> // for move
+#include <vector> // for vector
+
+#include "model/devices/hci_protocol.h" // for PacketReadCallback
+#include "os/log.h" // for LOG_ALWAYS_FATAL, LOG_INFO
+
+namespace test_vendor_lib {
+
+void H4Parser::Reset() {
+ state_ = HCI_TYPE;
+ packet_.clear();
+ bytes_wanted_ = 0;
+ packet_type_ = 0;
+}
+
+size_t H4Parser::HciGetPacketLengthForType(PacketType type,
+ const uint8_t* preamble) const {
+ static const size_t
+ packet_length_offset[static_cast<size_t>(PacketType::ISO) + 1] = {
+ 0,
+ H4Parser::COMMAND_LENGTH_OFFSET,
+ H4Parser::ACL_LENGTH_OFFSET,
+ H4Parser::SCO_LENGTH_OFFSET,
+ H4Parser::EVENT_LENGTH_OFFSET,
+ H4Parser::ISO_LENGTH_OFFSET,
+ };
+
+ size_t offset = packet_length_offset[static_cast<size_t>(type)];
+ size_t size = preamble[offset];
+ if (type == PacketType::ACL) {
+ size |= ((size_t)preamble[offset + 1]) << 8u;
+ }
+ if (type == PacketType::ISO) {
+ size |= ((size_t)preamble[offset + 1] & 0x0fu) << 8u;
+ }
+ return size;
+}
+
+H4Parser::H4Parser(PacketReadCallback command_cb, PacketReadCallback event_cb,
+ PacketReadCallback acl_cb, PacketReadCallback sco_cb,
+ PacketReadCallback iso_cb)
+ : command_cb_(std::move(command_cb)),
+ event_cb_(std::move(event_cb)),
+ acl_cb_(std::move(acl_cb)),
+ sco_cb_(std::move(sco_cb)),
+ iso_cb_(std::move(iso_cb)) {}
+
+void H4Parser::OnPacketReady() {
+ switch (hci_packet_type_) {
+ case PacketType::COMMAND:
+ command_cb_(packet_);
+ break;
+ case PacketType::ACL:
+ acl_cb_(packet_);
+ break;
+ case PacketType::SCO:
+ sco_cb_(packet_);
+ break;
+ case PacketType::EVENT:
+ event_cb_(packet_);
+ break;
+ case PacketType::ISO:
+ iso_cb_(packet_);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unimplemented packet type %d",
+ static_cast<int>(hci_packet_type_));
+ }
+ // Get ready for the next type byte.
+ hci_packet_type_ = PacketType::UNKNOWN;
+}
+
+size_t H4Parser::BytesRequested() {
+ switch (state_) {
+ case HCI_TYPE:
+ return 1;
+ case HCI_PREAMBLE:
+ case HCI_PAYLOAD:
+ return bytes_wanted_;
+ }
+}
+
+bool H4Parser::Consume(uint8_t* buffer, int32_t bytes_read) {
+ size_t bytes_to_read = BytesRequested();
+ if (bytes_read <= 0) {
+ LOG_INFO("remote disconnected, or unhandled error?");
+ return false;
+ } else if (bytes_read > BytesRequested()) {
+ LOG_ALWAYS_FATAL("More bytes read (%u) than expected (%u)!",
+ static_cast<int>(bytes_read),
+ static_cast<int>(bytes_to_read));
+ }
+
+ static const size_t preamble_size[static_cast<size_t>(PacketType::ISO) + 1] =
+ {
+ 0,
+ H4Parser::COMMAND_PREAMBLE_SIZE,
+ H4Parser::ACL_PREAMBLE_SIZE,
+ H4Parser::SCO_PREAMBLE_SIZE,
+ H4Parser::EVENT_PREAMBLE_SIZE,
+ H4Parser::ISO_PREAMBLE_SIZE,
+ };
+ switch (state_) {
+ case HCI_TYPE:
+ // bytes_read >= 1
+ packet_type_ = *buffer;
+ packet_.clear();
+ break;
+ case HCI_PREAMBLE:
+ case HCI_PAYLOAD:
+ packet_.insert(packet_.end(), buffer, buffer + bytes_read);
+ bytes_wanted_ -= bytes_read;
+ break;
+ }
+
+ switch (state_) {
+ case HCI_TYPE:
+ hci_packet_type_ = static_cast<PacketType>(packet_type_);
+ if (hci_packet_type_ != PacketType::ACL &&
+ hci_packet_type_ != PacketType::SCO &&
+ hci_packet_type_ != PacketType::COMMAND &&
+ hci_packet_type_ != PacketType::EVENT &&
+ hci_packet_type_ != PacketType::ISO) {
+ LOG_ALWAYS_FATAL("Unimplemented packet type %hhd", packet_type_);
+ }
+ state_ = HCI_PREAMBLE;
+ bytes_wanted_ = preamble_size[static_cast<size_t>(hci_packet_type_)];
+ break;
+ case HCI_PREAMBLE:
+
+ if (bytes_wanted_ == 0) {
+ size_t payload_size =
+ HciGetPacketLengthForType(hci_packet_type_, packet_.data());
+ if (payload_size == 0) {
+ OnPacketReady();
+ state_ = HCI_TYPE;
+ } else {
+ bytes_wanted_ = payload_size;
+ state_ = HCI_PAYLOAD;
+ }
+ }
+ break;
+ case HCI_PAYLOAD:
+ if (bytes_wanted_ == 0) {
+ OnPacketReady();
+ state_ = HCI_TYPE;
+ }
+ break;
+ }
+ return true;
+}
+} // namespace test_vendor_lib
--- /dev/null
+//
+// Copyright 2021 The Android Open Source Project
+//
+// 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.
+//
+
+#pragma once
+
+#include <stddef.h> // for size_t
+#include <cstdint> // for uint8_t, int32_t
+#include <functional> // for function
+#include <ostream> // for operator<<, ostream
+#include <vector> // for vector
+
+#include "model/devices/hci_protocol.h" // for PacketReadCallback
+
+namespace test_vendor_lib {
+
+using HciPacketReadyCallback = std::function<void(void)>;
+using ClientDisconnectCallback = std::function<void()>;
+
+enum class PacketType : uint8_t {
+ UNKNOWN = 0,
+ COMMAND = 1,
+ ACL = 2,
+ SCO = 3,
+ EVENT = 4,
+ ISO = 5,
+};
+
+// An H4 Parser can parse H4 Packets and will invoke the proper callback
+// once a packet has been parsed.
+//
+// You use it as follows:
+//
+// H4Parser h4(....);
+// size_t nr_bytes = h4.BytesRequested();
+// std::vector fill_this_vector_with_at_most_nr_bytes(nr_bytes);
+// h4.Consume(fill_this_vector_with_at_most_nr_bytes.data(), nr_bytes.size());
+//
+// The parser will invoke the proper callbacks once a packet has been parsed.
+// The parser keeps internal state and is not thread safe.
+class H4Parser {
+ public:
+ enum State { HCI_TYPE, HCI_PREAMBLE, HCI_PAYLOAD };
+
+ H4Parser(PacketReadCallback command_cb, PacketReadCallback event_cb,
+ PacketReadCallback acl_cb, PacketReadCallback sco_cb,
+ PacketReadCallback iso_cb);
+
+ // Consumes the given number of bytes, returns true on success.
+ bool Consume(uint8_t* buffer, int32_t bytes);
+
+ // The maximum number of bytes the parser can consume in the current state.
+ size_t BytesRequested();
+
+ // Resets the parser to the empty, initial state.
+ void Reset();
+
+ State CurrentState() { return state_; };
+
+ private:
+ void OnPacketReady();
+
+ // 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
+ static constexpr size_t COMMAND_PREAMBLE_SIZE = 3;
+ static constexpr size_t COMMAND_LENGTH_OFFSET = 2;
+
+ // 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
+ static constexpr size_t ACL_PREAMBLE_SIZE = 4;
+ static constexpr size_t ACL_LENGTH_OFFSET = 2;
+
+ // 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
+ static constexpr size_t SCO_PREAMBLE_SIZE = 3;
+ static constexpr size_t SCO_LENGTH_OFFSET = 2;
+
+ // 1 byte for event code, 1 byte for parameter length (Volume 2, Part
+ // E, 5.4.4)
+ static constexpr size_t EVENT_PREAMBLE_SIZE = 2;
+ static constexpr size_t EVENT_LENGTH_OFFSET = 1;
+
+ // 2 bytes for handle and flags, 12 bits for length (Volume 2, Part E, 5.4.5)
+ static constexpr size_t ISO_PREAMBLE_SIZE = 4;
+ static constexpr size_t ISO_LENGTH_OFFSET = 2;
+
+ PacketReadCallback command_cb_;
+ PacketReadCallback event_cb_;
+ PacketReadCallback acl_cb_;
+ PacketReadCallback sco_cb_;
+ PacketReadCallback iso_cb_;
+
+ size_t HciGetPacketLengthForType(PacketType type,
+ const uint8_t* preamble) const;
+
+ PacketType hci_packet_type_{PacketType::UNKNOWN};
+
+ State state_{HCI_TYPE};
+ uint8_t packet_type_{};
+ std::vector<uint8_t> packet_;
+ size_t bytes_wanted_{0};
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ H4Parser::State const& state_) {
+ os << (state_ == H4Parser::State::HCI_TYPE
+ ? "HCI_TYPE"
+ : state_ == H4Parser::State::HCI_PREAMBLE ? "HCI_PREAMBLE"
+ : "HCI_PAYLOAD");
+ return os;
+}
+
+} // namespace test_vendor_lib
--- /dev/null
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "model/devices/h4_parser.h"
+
+#include <gtest/gtest.h>
+
+#include "osi/include/osi.h" // for OSI_NO_INTR
+
+namespace test_vendor_lib {
+using PacketData = std::vector<uint8_t>;
+
+class H4ParserTest : public ::testing::Test {
+ public:
+ protected:
+ void SetUp() override {
+ packet_.clear();
+ parser_.Reset();
+ }
+
+ void TearDown() override { parser_.Reset(); }
+
+ void PacketReadCallback(const std::vector<uint8_t>& packet) {
+ packet_ = std::move(packet);
+ }
+
+ protected:
+ H4Parser parser_{
+ [&](auto p) {
+ type_ = PacketType::COMMAND;
+ PacketReadCallback(p);
+ },
+ [&](auto p) {
+ type_ = PacketType::EVENT;
+ PacketReadCallback(p);
+ },
+ [&](auto p) {
+ type_ = PacketType::ACL;
+ PacketReadCallback(p);
+ },
+ [&](auto p) {
+ type_ = PacketType::SCO;
+ PacketReadCallback(p);
+ },
+ [&](auto p) {
+ type_ = PacketType::ISO;
+ PacketReadCallback(p);
+ },
+ };
+ PacketData packet_;
+ PacketType type_;
+};
+
+TEST_F(H4ParserTest, InitiallyExpectOneByte) {
+ ASSERT_EQ(1, (int)parser_.BytesRequested());
+}
+
+TEST_F(H4ParserTest, SwitchStateAfterType) {
+ uint8_t typ = (uint8_t)PacketType::ACL;
+ ASSERT_TRUE(parser_.Consume(&typ, 1));
+ ASSERT_EQ(parser_.CurrentState(), H4Parser::State::HCI_PREAMBLE);
+}
+
+TEST_F(H4ParserTest, RequestedBytesDecreases) {
+ // Make sure that the requested bytes is monotonically decreasing
+ // for the requested types.
+ uint8_t typ = (uint8_t)PacketType::ACL;
+ ASSERT_TRUE(parser_.Consume(&typ, 1));
+ auto wanted = parser_.BytesRequested();
+ while (wanted > 0) {
+ ASSERT_EQ(wanted, parser_.BytesRequested());
+ ASSERT_TRUE(parser_.Consume(&typ, 1));
+ wanted--;
+ }
+
+ ASSERT_EQ(parser_.CurrentState(), H4Parser::State::HCI_PAYLOAD);
+ wanted = parser_.BytesRequested();
+ while (wanted > 0) {
+ ASSERT_EQ(wanted, parser_.BytesRequested());
+ ASSERT_TRUE(parser_.Consume(&typ, 1));
+ wanted--;
+ }
+
+ // A callback should have been invoked.
+ ASSERT_LT(0, (int)packet_.size());
+}
+
+TEST_F(H4ParserTest, RejectNoData) {
+ // You need to give us something!
+ PacketData bad_bit;
+ ASSERT_FALSE(parser_.Consume(bad_bit.data(), bad_bit.size()));
+}
+
+TEST_F(H4ParserTest, TooMuchIsDeath) {
+ PacketData bad_bit({0xfd});
+ ASSERT_DEATH(parser_.Consume(bad_bit.data(), parser_.BytesRequested() + 1),
+ "More bytes read (.*) than expected (.*)!");
+}
+
+TEST_F(H4ParserTest, WrongTypeIsDeath) {
+ PacketData bad_bit({0xfd});
+ ASSERT_DEATH(parser_.Consume(bad_bit.data(), bad_bit.size()),
+ "Unimplemented packet type.*");
+}
+
+TEST_F(H4ParserTest, CallsTheRightCallbacks) {
+ // Make sure that the proper type of callback is invoked.
+ std::vector<PacketType> types({PacketType::ACL, PacketType::SCO,
+ PacketType::COMMAND, PacketType::EVENT,
+ PacketType::ISO});
+ for (auto packetType : types) {
+ // Configure the incoming packet.
+ uint8_t typ = (uint8_t)packetType;
+ ASSERT_TRUE(parser_.Consume(&typ, 1));
+
+ // Feed data as long as this packet is not complete.
+ while (parser_.CurrentState() != H4Parser::State::HCI_TYPE) {
+ PacketData data;
+ for (uint32_t i = 0; i < parser_.BytesRequested(); i++) {
+ data.push_back((uint8_t)i);
+ }
+ ASSERT_TRUE(parser_.Consume(data.data(), data.size()));
+ }
+
+ // The proper callbacks should have been invoked.
+ ASSERT_LT(0, (int)packet_.size());
+ ASSERT_EQ(packetType, type_);
+ }
+}
+
+} // namespace test_vendor_lib