Merge "Bring multi-channel transport into the glorious new age"

am: a15a9bbadf

Change-Id: Iec27272215b8e11041c221ad9d338e8011052ae3
This commit is contained in:
Zach Johnson 2017-03-08 17:55:38 +00:00 committed by android-build-merger
commit 2296cfbe55
14 changed files with 907 additions and 154 deletions

View file

@ -56,6 +56,9 @@ cc_library_static {
name: "android.hardware.bluetooth-hci",
srcs: [
"hci_packetizer.cc",
"hci_protocol.cc",
"h4_protocol.cc",
"mct_protocol.cc",
],
export_include_dirs: ["."],
shared_libs: [
@ -71,16 +74,21 @@ cc_test {
name: "bluetooth-vendor-interface-unit-tests",
srcs: [
"test/async_fd_watcher_unittest.cc",
"test/h4_protocol_unittest.cc",
"test/mct_protocol_unittest.cc",
],
local_include_dirs: [
"test",
],
shared_libs: [
"libbase",
"libhidlbase",
"liblog",
],
static_libs: [
"android.hardware.bluetooth-async",
"android.hardware.bluetooth-hci",
"libgmock",
],
}

View file

@ -44,21 +44,14 @@ Return<void> BluetoothHci::initialize(
event_cb_->initializationComplete(
status ? Status::SUCCESS : Status::INITIALIZATION_ERROR);
},
[this](HciPacketType type, const hidl_vec<uint8_t>& packet) {
switch (type) {
case HCI_PACKET_TYPE_EVENT:
event_cb_->hciEventReceived(packet);
break;
case HCI_PACKET_TYPE_ACL_DATA:
event_cb_->aclDataReceived(packet);
break;
case HCI_PACKET_TYPE_SCO_DATA:
event_cb_->scoDataReceived(packet);
break;
default:
ALOGE("%s Unexpected event type %d", __func__, type);
break;
}
[this](const hidl_vec<uint8_t>& packet) {
event_cb_->hciEventReceived(packet);
},
[this](const hidl_vec<uint8_t>& packet) {
event_cb_->aclDataReceived(packet);
},
[this](const hidl_vec<uint8_t>& packet) {
event_cb_->scoDataReceived(packet);
});
if (!rc) event_cb_->initializationComplete(Status::INITIALIZATION_ERROR);
return Void();

View file

@ -0,0 +1,71 @@
//
// Copyright 2017 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 "h4_protocol.h"
#define LOG_TAG "android.hardware.bluetooth-hci-h4"
#include <android-base/logging.h>
#include <assert.h>
#include <fcntl.h>
namespace android {
namespace hardware {
namespace bluetooth {
namespace hci {
size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) {
int rv = WriteSafely(uart_fd_, &type, sizeof(type));
if (rv == sizeof(type)) {
rv = WriteSafely(uart_fd_, data, length);
}
return rv;
}
void H4Protocol::OnPacketReady() {
switch (hci_packet_type_) {
case HCI_PACKET_TYPE_EVENT:
event_cb_(hci_packetizer_.GetPacket());
break;
case HCI_PACKET_TYPE_ACL_DATA:
acl_cb_(hci_packetizer_.GetPacket());
break;
case HCI_PACKET_TYPE_SCO_DATA:
sco_cb_(hci_packetizer_.GetPacket());
break;
default: {
bool bad_packet_type = true;
CHECK(!bad_packet_type);
}
}
// Get ready for the next type byte.
hci_packet_type_ = HCI_PACKET_TYPE_UNKNOWN;
}
void H4Protocol::OnDataReady(int fd) {
if (hci_packet_type_ == HCI_PACKET_TYPE_UNKNOWN) {
uint8_t buffer[1] = {0};
size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, 1));
CHECK(bytes_read == 1);
hci_packet_type_ = static_cast<HciPacketType>(buffer[0]);
} else {
hci_packetizer_.OnDataReady(fd, hci_packet_type_);
}
}
} // namespace hci
} // namespace bluetooth
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,61 @@
//
// Copyright 2017 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 <hidl/HidlSupport.h>
#include "async_fd_watcher.h"
#include "bt_vendor_lib.h"
#include "hci_internals.h"
#include "hci_protocol.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace hci {
class H4Protocol : public HciProtocol {
public:
H4Protocol(int fd, PacketReadCallback event_cb, PacketReadCallback acl_cb,
PacketReadCallback sco_cb)
: uart_fd_(fd),
event_cb_(event_cb),
acl_cb_(acl_cb),
sco_cb_(sco_cb),
hci_packetizer_([this]() { OnPacketReady(); }) {}
size_t Send(uint8_t type, const uint8_t* data, size_t length);
void OnPacketReady();
void OnDataReady(int fd);
private:
int uart_fd_;
PacketReadCallback event_cb_;
PacketReadCallback acl_cb_;
PacketReadCallback sco_cb_;
HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN};
hci::HciPacketizer hci_packetizer_;
};
} // namespace hci
} // namespace bluetooth
} // namespace hardware
} // namespace android

View file

@ -18,7 +18,6 @@
#define LOG_TAG "android.hardware.bluetooth.hci_packetizer"
#include <android-base/logging.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <dlfcn.h>
@ -46,63 +45,40 @@ namespace hardware {
namespace bluetooth {
namespace hci {
HciPacketType HciPacketizer::GetPacketType() const {
return hci_packet_type_;
}
const hidl_vec<uint8_t>& HciPacketizer::GetPacket() const { return packet_; }
const hidl_vec<uint8_t>& HciPacketizer::GetPacket() const {
return hci_packet_;
}
void HciPacketizer::OnDataReady(int fd) {
switch (hci_parser_state_) {
case HCI_IDLE: {
uint8_t buffer[1] = {0};
size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, 1));
CHECK(bytes_read == 1);
hci_packet_type_ = static_cast<HciPacketType>(buffer[0]);
CHECK(hci_packet_type_ >= HCI_PACKET_TYPE_ACL_DATA &&
hci_packet_type_ <= HCI_PACKET_TYPE_EVENT)
<< "buffer[0] = " << static_cast<unsigned int>(buffer[0]);
hci_parser_state_ = HCI_TYPE_READY;
hci_packet_bytes_remaining_ = preamble_size_for_type[hci_packet_type_];
hci_packet_bytes_read_ = 0;
break;
}
case HCI_TYPE_READY: {
void HciPacketizer::OnDataReady(int fd, HciPacketType packet_type) {
switch (state_) {
case HCI_PREAMBLE: {
size_t bytes_read = TEMP_FAILURE_RETRY(
read(fd, hci_packet_preamble_ + hci_packet_bytes_read_,
hci_packet_bytes_remaining_));
read(fd, preamble_ + bytes_read_,
preamble_size_for_type[packet_type] - bytes_read_));
CHECK(bytes_read > 0);
hci_packet_bytes_remaining_ -= bytes_read;
hci_packet_bytes_read_ += bytes_read;
if (hci_packet_bytes_remaining_ == 0) {
bytes_read_ += bytes_read;
if (bytes_read_ == preamble_size_for_type[packet_type]) {
size_t packet_length =
HciGetPacketLengthForType(hci_packet_type_, hci_packet_preamble_);
hci_packet_.resize(preamble_size_for_type[hci_packet_type_] +
packet_length);
memcpy(hci_packet_.data(), hci_packet_preamble_,
preamble_size_for_type[hci_packet_type_]);
hci_packet_bytes_remaining_ = packet_length;
hci_parser_state_ = HCI_PAYLOAD;
hci_packet_bytes_read_ = 0;
HciGetPacketLengthForType(packet_type, preamble_);
packet_.resize(preamble_size_for_type[packet_type] + packet_length);
memcpy(packet_.data(), preamble_, preamble_size_for_type[packet_type]);
bytes_remaining_ = packet_length;
state_ = HCI_PAYLOAD;
bytes_read_ = 0;
}
break;
}
case HCI_PAYLOAD: {
size_t bytes_read = TEMP_FAILURE_RETRY(
read(fd,
hci_packet_.data() + preamble_size_for_type[hci_packet_type_] +
hci_packet_bytes_read_,
hci_packet_bytes_remaining_));
size_t bytes_read = TEMP_FAILURE_RETRY(read(
fd,
packet_.data() + preamble_size_for_type[packet_type] + bytes_read_,
bytes_remaining_));
CHECK(bytes_read > 0);
hci_packet_bytes_remaining_ -= bytes_read;
hci_packet_bytes_read_ += bytes_read;
if (hci_packet_bytes_remaining_ == 0) {
hci_packet_ready_cb_();
hci_parser_state_ = HCI_IDLE;
bytes_remaining_ -= bytes_read;
bytes_read_ += bytes_read;
if (bytes_remaining_ == 0) {
packet_ready_cb_();
state_ = HCI_PREAMBLE;
bytes_read_ = 0;
}
break;
}

View file

@ -32,20 +32,19 @@ using HciPacketReadyCallback = std::function<void(void)>;
class HciPacketizer {
public:
HciPacketizer(HciPacketReadyCallback packet_cb) : hci_packet_ready_cb_(packet_cb) {};
void OnDataReady(int fd);
HciPacketizer(HciPacketReadyCallback packet_cb)
: packet_ready_cb_(packet_cb){};
void OnDataReady(int fd, HciPacketType packet_type);
const hidl_vec<uint8_t>& GetPacket() const;
HciPacketType GetPacketType() const;
protected:
enum HciParserState { HCI_IDLE, HCI_TYPE_READY, HCI_PAYLOAD };
HciParserState hci_parser_state_{HCI_IDLE};
HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN};
uint8_t hci_packet_preamble_[HCI_PREAMBLE_SIZE_MAX];
hidl_vec<uint8_t> hci_packet_;
size_t hci_packet_bytes_remaining_;
size_t hci_packet_bytes_read_;
HciPacketReadyCallback hci_packet_ready_cb_;
enum State { HCI_PREAMBLE, HCI_PAYLOAD };
State state_{HCI_PREAMBLE};
uint8_t preamble_[HCI_PREAMBLE_SIZE_MAX];
hidl_vec<uint8_t> packet_;
size_t bytes_remaining_{0};
size_t bytes_read_{0};
HciPacketReadyCallback packet_ready_cb_;
};
} // namespace hci

View file

@ -0,0 +1,74 @@
//
// Copyright 2017 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 "hci_protocol.h"
#define LOG_TAG "android.hardware.bluetooth-hci-hci_protocol"
#include <android-base/logging.h>
#include <assert.h>
#include <fcntl.h>
#include <utils/Log.h>
namespace {
const size_t preamble_size_for_type[] = {
0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE,
HCI_EVENT_PREAMBLE_SIZE};
const size_t packet_length_offset_for_type[] = {
0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO,
HCI_LENGTH_OFFSET_EVT};
size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) {
size_t offset = packet_length_offset_for_type[type];
if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset];
return (((preamble[offset + 1]) << 8) | preamble[offset]);
}
} // namespace
namespace android {
namespace hardware {
namespace bluetooth {
namespace hci {
size_t HciProtocol::WriteSafely(int fd, const uint8_t* data, size_t length) {
size_t transmitted_length = 0;
while (length > 0) {
ssize_t ret =
TEMP_FAILURE_RETRY(write(fd, data + transmitted_length, length));
if (ret == -1) {
if (errno == EAGAIN) continue;
ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
break;
} else if (ret == 0) {
// Nothing written :(
ALOGE("%s zero bytes written - something went wrong...", __func__);
break;
}
transmitted_length += ret;
length -= ret;
}
return transmitted_length;
}
} // namespace hci
} // namespace bluetooth
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,49 @@
//
// Copyright 2017 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 <hidl/HidlSupport.h>
#include "bt_vendor_lib.h"
#include "hci_internals.h"
#include "hci_packetizer.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace hci {
using ::android::hardware::hidl_vec;
using PacketReadCallback = std::function<void(const hidl_vec<uint8_t>&)>;
// Implementation of HCI protocol bits common to different transports
class HciProtocol {
public:
HciProtocol() = default;
virtual ~HciProtocol(){};
// Protocol-specific implementation of sending packets.
virtual size_t Send(uint8_t type, const uint8_t* data, size_t length) = 0;
protected:
static size_t WriteSafely(int fd, const uint8_t* data, size_t length);
};
} // namespace hci
} // namespace bluetooth
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,71 @@
//
// Copyright 2017 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 "mct_protocol.h"
#include <assert.h>
#define LOG_TAG "android.hardware.bluetooth-hci-mct"
#include <android-base/logging.h>
#include <utils/Log.h>
#include <fcntl.h>
namespace android {
namespace hardware {
namespace bluetooth {
namespace hci {
MctProtocol::MctProtocol(int* fds, PacketReadCallback event_cb,
PacketReadCallback acl_cb)
: event_cb_(event_cb),
acl_cb_(acl_cb),
event_packetizer_([this]() { OnEventPacketReady(); }),
acl_packetizer_([this]() { OnAclDataPacketReady(); }) {
for (int i = 0; i < CH_MAX; i++) {
uart_fds_[i] = fds[i];
}
}
size_t MctProtocol::Send(uint8_t type, const uint8_t* data, size_t length) {
if (type == HCI_PACKET_TYPE_COMMAND)
return WriteSafely(uart_fds_[CH_CMD], data, length);
if (type == HCI_PACKET_TYPE_ACL_DATA)
return WriteSafely(uart_fds_[CH_ACL_OUT], data, length);
CHECK(type == HCI_PACKET_TYPE_COMMAND || type == HCI_PACKET_TYPE_ACL_DATA);
return 0;
}
void MctProtocol::OnEventPacketReady() {
event_cb_(event_packetizer_.GetPacket());
}
void MctProtocol::OnAclDataPacketReady() {
acl_cb_(acl_packetizer_.GetPacket());
}
void MctProtocol::OnEventDataReady(int fd) {
event_packetizer_.OnDataReady(fd, HCI_PACKET_TYPE_EVENT);
}
void MctProtocol::OnAclDataReady(int fd) {
acl_packetizer_.OnDataReady(fd, HCI_PACKET_TYPE_ACL_DATA);
}
} // namespace hci
} // namespace bluetooth
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,56 @@
//
// Copyright 2017 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 <hidl/HidlSupport.h>
#include "async_fd_watcher.h"
#include "bt_vendor_lib.h"
#include "hci_internals.h"
#include "hci_protocol.h"
namespace android {
namespace hardware {
namespace bluetooth {
namespace hci {
class MctProtocol : public HciProtocol {
public:
MctProtocol(int* fds, PacketReadCallback event_cb, PacketReadCallback acl_cb);
size_t Send(uint8_t type, const uint8_t* data, size_t length);
void OnEventPacketReady();
void OnAclDataPacketReady();
void OnEventDataReady(int fd);
void OnAclDataReady(int fd);
private:
int uart_fds_[CH_MAX];
PacketReadCallback event_cb_;
PacketReadCallback acl_cb_;
hci::HciPacketizer event_packetizer_;
hci::HciPacketizer acl_packetizer_;
};
} // namespace hci
} // namespace bluetooth
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,213 @@
//
// Copyright 2017 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.
//
#define LOG_TAG "bt_h4_unittest"
#include "h4_protocol.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <condition_variable>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <vector>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <utils/Log.h>
namespace android {
namespace hardware {
namespace bluetooth {
namespace V1_0 {
namespace implementation {
using ::testing::Eq;
using hci::H4Protocol;
static char sample_data1[100] = "A point is that which has no part.";
static char sample_data2[100] = "A line is breadthless length.";
static char sample_data3[100] = "The ends of a line are points.";
static char acl_data[100] =
"A straight line is a line which lies evenly with the points on itself.";
static char sco_data[100] =
"A surface is that which has length and breadth only.";
static char event_data[100] = "The edges of a surface are lines.";
MATCHER_P3(HidlVecMatches, preamble, preamble_length, payload, "") {
size_t length = strlen(payload) + preamble_length;
if (length != arg.size()) {
return false;
}
if (memcmp(preamble, arg.data(), preamble_length) != 0) {
return false;
}
return memcmp(payload, arg.data() + preamble_length,
length - preamble_length) == 0;
};
ACTION_P2(Notify, mutex, condition) {
ALOGD("%s", __func__);
std::unique_lock<std::mutex> lock(*mutex);
condition->notify_one();
}
class H4ProtocolTest : public ::testing::Test {
protected:
void SetUp() override {
ALOGD("%s", __func__);
int sockfd[2];
socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
H4Protocol* h4_hci =
new H4Protocol(sockfd[0], event_cb_.AsStdFunction(),
acl_cb_.AsStdFunction(), sco_cb_.AsStdFunction());
fd_watcher_.WatchFdForNonBlockingReads(
sockfd[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
protocol_ = h4_hci;
fake_uart_ = sockfd[1];
}
void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); }
void SendAndReadUartOutbound(uint8_t type, char* data) {
ALOGD("%s sending", __func__);
int data_length = strlen(data);
protocol_->Send(type, (uint8_t*)data, data_length);
int uart_length = data_length + 1; // + 1 for data type code
int i;
ALOGD("%s reading", __func__);
for (i = 0; i < uart_length; i++) {
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(fake_uart_, &read_fds);
TEMP_FAILURE_RETRY(select(fake_uart_ + 1, &read_fds, NULL, NULL, NULL));
char byte;
TEMP_FAILURE_RETRY(read(fake_uart_, &byte, 1));
EXPECT_EQ(i == 0 ? type : data[i - 1], byte);
}
EXPECT_EQ(i, uart_length);
}
void WriteAndExpectInboundAclData(char* payload) {
// h4 type[1] + handle[2] + size[2]
char preamble[5] = {HCI_PACKET_TYPE_ACL_DATA, 19, 92, 0, 0};
int length = strlen(payload);
preamble[3] = length & 0xFF;
preamble[4] = (length >> 8) & 0xFF;
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble)));
TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload)));
ALOGD("%s waiting", __func__);
std::mutex mutex;
std::condition_variable done;
EXPECT_CALL(acl_cb_, Call(HidlVecMatches(preamble + 1, sizeof(preamble) - 1,
payload)))
.WillOnce(Notify(&mutex, &done));
// Fail if it takes longer than 100 ms.
auto timeout_time =
std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
{
std::unique_lock<std::mutex> lock(mutex);
done.wait_until(lock, timeout_time);
}
}
void WriteAndExpectInboundScoData(char* payload) {
// h4 type[1] + handle[2] + size[1]
char preamble[4] = {HCI_PACKET_TYPE_SCO_DATA, 20, 17, 0};
preamble[3] = strlen(payload) & 0xFF;
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble)));
TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload)));
ALOGD("%s waiting", __func__);
std::mutex mutex;
std::condition_variable done;
EXPECT_CALL(sco_cb_, Call(HidlVecMatches(preamble + 1, sizeof(preamble) - 1,
payload)))
.WillOnce(Notify(&mutex, &done));
// Fail if it takes longer than 100 ms.
auto timeout_time =
std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
{
std::unique_lock<std::mutex> lock(mutex);
done.wait_until(lock, timeout_time);
}
}
void WriteAndExpectInboundEvent(char* payload) {
// h4 type[1] + event_code[1] + size[1]
char preamble[3] = {HCI_PACKET_TYPE_EVENT, 9, 0};
preamble[2] = strlen(payload) & 0xFF;
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble)));
TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload)));
ALOGD("%s waiting", __func__);
std::mutex mutex;
std::condition_variable done;
EXPECT_CALL(event_cb_, Call(HidlVecMatches(preamble + 1,
sizeof(preamble) - 1, payload)))
.WillOnce(Notify(&mutex, &done));
{
std::unique_lock<std::mutex> lock(mutex);
done.wait(lock);
}
}
testing::MockFunction<void(const hidl_vec<uint8_t>&)> event_cb_;
testing::MockFunction<void(const hidl_vec<uint8_t>&)> acl_cb_;
testing::MockFunction<void(const hidl_vec<uint8_t>&)> sco_cb_;
async::AsyncFdWatcher fd_watcher_;
H4Protocol* protocol_;
int fake_uart_;
};
// Test sending data sends correct data onto the UART
TEST_F(H4ProtocolTest, TestSends) {
SendAndReadUartOutbound(HCI_PACKET_TYPE_COMMAND, sample_data1);
SendAndReadUartOutbound(HCI_PACKET_TYPE_ACL_DATA, sample_data2);
SendAndReadUartOutbound(HCI_PACKET_TYPE_SCO_DATA, sample_data3);
}
// Ensure we properly parse data coming from the UART
TEST_F(H4ProtocolTest, TestReads) {
WriteAndExpectInboundAclData(acl_data);
WriteAndExpectInboundScoData(sco_data);
WriteAndExpectInboundEvent(event_data);
}
} // namespace implementation
} // namespace V1_0
} // namespace bluetooth
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,200 @@
//
// Copyright 2017 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.
//
#define LOG_TAG "bt_h4_unittest"
#include "mct_protocol.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <condition_variable>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <vector>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <utils/Log.h>
namespace android {
namespace hardware {
namespace bluetooth {
namespace V1_0 {
namespace implementation {
using ::testing::Eq;
using hci::MctProtocol;
static char sample_data1[100] = "A point is that which has no part.";
static char sample_data2[100] = "A line is breadthless length.";
static char sample_data3[100] = "The ends of a line are points.";
static char acl_data[100] =
"A straight line is a line which lies evenly with the points on itself.";
static char sco_data[100] =
"A surface is that which has length and breadth only.";
static char event_data[100] = "The edges of a surface are lines.";
MATCHER_P3(HidlVecMatches, preamble, preamble_length, payload, "") {
size_t length = strlen(payload) + preamble_length;
if (length != arg.size()) {
return false;
}
if (memcmp(preamble, arg.data(), preamble_length) != 0) {
return false;
}
return memcmp(payload, arg.data() + preamble_length,
length - preamble_length) == 0;
};
ACTION_P2(Notify, mutex, condition) {
ALOGD("%s", __func__);
std::unique_lock<std::mutex> lock(*mutex);
condition->notify_one();
}
class MctProtocolTest : public ::testing::Test {
protected:
void SetUp() override {
ALOGD("%s", __func__);
int mct_fds[CH_MAX];
MakeFakeUartFd(CH_CMD, mct_fds, fake_uart_);
MakeFakeUartFd(CH_EVT, mct_fds, fake_uart_);
MakeFakeUartFd(CH_ACL_IN, mct_fds, fake_uart_);
MakeFakeUartFd(CH_ACL_OUT, mct_fds, fake_uart_);
MctProtocol* mct_hci = new MctProtocol(mct_fds, event_cb_.AsStdFunction(),
acl_cb_.AsStdFunction());
fd_watcher_.WatchFdForNonBlockingReads(
mct_fds[CH_EVT], [mct_hci](int fd) { mct_hci->OnEventDataReady(fd); });
fd_watcher_.WatchFdForNonBlockingReads(
mct_fds[CH_ACL_IN], [mct_hci](int fd) { mct_hci->OnAclDataReady(fd); });
protocol_ = mct_hci;
}
void MakeFakeUartFd(int index, int* host_side, int* controller_side) {
int sockfd[2];
socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
host_side[index] = sockfd[0];
controller_side[index] = sockfd[1];
}
void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); }
void SendAndReadUartOutbound(uint8_t type, char* data, int outbound_fd) {
ALOGD("%s sending", __func__);
int data_length = strlen(data);
protocol_->Send(type, (uint8_t*)data, data_length);
ALOGD("%s reading", __func__);
int i;
for (i = 0; i < data_length; i++) {
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(outbound_fd, &read_fds);
TEMP_FAILURE_RETRY(select(outbound_fd + 1, &read_fds, NULL, NULL, NULL));
char byte;
TEMP_FAILURE_RETRY(read(outbound_fd, &byte, 1));
EXPECT_EQ(data[i], byte);
}
EXPECT_EQ(i, data_length);
}
void WriteAndExpectInboundAclData(char* payload) {
// handle[2] + size[2]
char preamble[4] = {19, 92, 0, 0};
int length = strlen(payload);
preamble[2] = length & 0xFF;
preamble[3] = (length >> 8) & 0xFF;
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(
write(fake_uart_[CH_ACL_IN], preamble, sizeof(preamble)));
TEMP_FAILURE_RETRY(write(fake_uart_[CH_ACL_IN], payload, strlen(payload)));
ALOGD("%s waiting", __func__);
std::mutex mutex;
std::condition_variable done;
EXPECT_CALL(acl_cb_,
Call(HidlVecMatches(preamble, sizeof(preamble), payload)))
.WillOnce(Notify(&mutex, &done));
// Fail if it takes longer than 100 ms.
auto timeout_time =
std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
{
std::unique_lock<std::mutex> lock(mutex);
done.wait_until(lock, timeout_time);
}
}
void WriteAndExpectInboundEvent(char* payload) {
// event_code[1] + size[1]
char preamble[2] = {9, 0};
preamble[1] = strlen(payload) & 0xFF;
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(write(fake_uart_[CH_EVT], preamble, sizeof(preamble)));
TEMP_FAILURE_RETRY(write(fake_uart_[CH_EVT], payload, strlen(payload)));
ALOGD("%s waiting", __func__);
std::mutex mutex;
std::condition_variable done;
EXPECT_CALL(event_cb_,
Call(HidlVecMatches(preamble, sizeof(preamble), payload)))
.WillOnce(Notify(&mutex, &done));
// Fail if it takes longer than 100 ms.
auto timeout_time =
std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
{
std::unique_lock<std::mutex> lock(mutex);
done.wait_until(lock, timeout_time);
}
}
testing::MockFunction<void(const hidl_vec<uint8_t>&)> event_cb_;
testing::MockFunction<void(const hidl_vec<uint8_t>&)> acl_cb_;
async::AsyncFdWatcher fd_watcher_;
MctProtocol* protocol_;
int fake_uart_[CH_MAX];
};
// Test sending data sends correct data onto the UART
TEST_F(MctProtocolTest, TestSends) {
SendAndReadUartOutbound(HCI_PACKET_TYPE_COMMAND, sample_data1,
fake_uart_[CH_CMD]);
SendAndReadUartOutbound(HCI_PACKET_TYPE_ACL_DATA, sample_data2,
fake_uart_[CH_ACL_OUT]);
}
// Ensure we properly parse data coming from the UART
TEST_F(MctProtocolTest, TestReads) {
WriteAndExpectInboundAclData(acl_data);
WriteAndExpectInboundEvent(event_data);
}
} // namespace implementation
} // namespace V1_0
} // namespace bluetooth
} // namespace hardware
} // namespace android

View file

@ -27,6 +27,8 @@
#include <fcntl.h>
#include "bluetooth_address.h"
#include "h4_protocol.h"
#include "mct_protocol.h"
static const char* VENDOR_LIBRARY_NAME = "libbt-vendor.so";
static const char* VENDOR_LIBRARY_SYMBOL_NAME =
@ -64,30 +66,6 @@ HC_BT_HDR* WrapPacketAndCopy(uint16_t event, const hidl_vec<uint8_t>& data) {
return packet;
}
size_t write_safely(int fd, const uint8_t* data, size_t length) {
size_t transmitted_length = 0;
while (length > 0) {
ssize_t ret =
TEMP_FAILURE_RETRY(write(fd, data + transmitted_length, length));
if (ret == -1) {
if (errno == EAGAIN) continue;
ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
break;
} else if (ret == 0) {
// Nothing written :(
ALOGE("%s zero bytes written - something went wrong...", __func__);
break;
}
transmitted_length += ret;
length -= ret;
}
return transmitted_length;
}
bool internal_command_event_match(const hidl_vec<uint8_t>& packet) {
uint8_t event_code = packet[0];
if (event_code != HCI_COMMAND_COMPLETE_EVENT) {
@ -185,10 +163,12 @@ class FirmwareStartupTimer {
bool VendorInterface::Initialize(
InitializeCompleteCallback initialize_complete_cb,
PacketReadCallback packet_read_cb) {
PacketReadCallback event_cb, PacketReadCallback acl_cb,
PacketReadCallback sco_cb) {
assert(!g_vendor_interface);
g_vendor_interface = new VendorInterface();
return g_vendor_interface->Open(initialize_complete_cb, packet_read_cb);
return g_vendor_interface->Open(initialize_complete_cb, event_cb, acl_cb,
sco_cb);
}
void VendorInterface::Shutdown() {
@ -201,9 +181,10 @@ void VendorInterface::Shutdown() {
VendorInterface* VendorInterface::get() { return g_vendor_interface; }
bool VendorInterface::Open(InitializeCompleteCallback initialize_complete_cb,
PacketReadCallback packet_read_cb) {
PacketReadCallback event_cb,
PacketReadCallback acl_cb,
PacketReadCallback sco_cb) {
initialize_complete_cb_ = initialize_complete_cb;
packet_read_cb_ = packet_read_cb;
// Initialize vendor interface
@ -241,28 +222,41 @@ bool VendorInterface::Open(InitializeCompleteCallback initialize_complete_cb,
power_state = BT_VND_PWR_ON;
lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
// Get the UART socket
// Get the UART socket(s)
int fd_list[CH_MAX] = {0};
int fd_count = lib_interface_->op(BT_VND_OP_USERIAL_OPEN, &fd_list);
if (fd_count != 1) {
ALOGE("%s fd count %d != 1; we can't handle this currently...", __func__,
fd_count);
return false;
for (int i = 0; i < fd_count; i++) {
if (fd_list[i] == INVALID_FD) {
ALOGE("%s: fd %d is invalid!", __func__, fd_list[i]);
return false;
}
}
uart_fd_ = fd_list[0];
if (uart_fd_ == INVALID_FD) {
ALOGE("%s unable to determine UART fd", __func__);
return false;
event_cb_ = event_cb;
PacketReadCallback intercept_events = [this](const hidl_vec<uint8_t>& event) {
HandleIncomingEvent(event);
};
if (fd_count == 1) {
hci::H4Protocol* h4_hci =
new hci::H4Protocol(fd_list[0], intercept_events, acl_cb, sco_cb);
fd_watcher_.WatchFdForNonBlockingReads(
fd_list[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
hci_ = h4_hci;
} else {
hci::MctProtocol* mct_hci =
new hci::MctProtocol(fd_list, intercept_events, acl_cb);
fd_watcher_.WatchFdForNonBlockingReads(
fd_list[CH_EVT], [mct_hci](int fd) { mct_hci->OnEventDataReady(fd); });
if (fd_count >= CH_ACL_IN)
fd_watcher_.WatchFdForNonBlockingReads(
fd_list[CH_ACL_IN],
[mct_hci](int fd) { mct_hci->OnAclDataReady(fd); });
hci_ = mct_hci;
}
ALOGI("%s UART fd: %d", __func__, uart_fd_);
fd_watcher_.WatchFdForNonBlockingReads(uart_fd_,
[this](int fd) { hci_packetizer_.OnDataReady(fd); });
// Initially, the power management is off.
lpm_wake_deasserted = true;
@ -276,12 +270,16 @@ bool VendorInterface::Open(InitializeCompleteCallback initialize_complete_cb,
void VendorInterface::Close() {
fd_watcher_.StopWatchingFileDescriptors();
if (hci_ != nullptr) {
delete hci_;
hci_ = nullptr;
}
if (lib_interface_ != nullptr) {
bt_vendor_lpm_mode_t mode = BT_VND_LPM_DISABLE;
lib_interface_->op(BT_VND_OP_LPM_SET_MODE, &mode);
lib_interface_->op(BT_VND_OP_USERIAL_CLOSE, nullptr);
uart_fd_ = INVALID_FD;
int power_state = BT_VND_PWR_OFF;
lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
}
@ -298,8 +296,6 @@ void VendorInterface::Close() {
}
size_t VendorInterface::Send(uint8_t type, const uint8_t* data, size_t length) {
if (uart_fd_ == INVALID_FD) return 0;
recent_activity_flag = true;
if (lpm_wake_deasserted == true) {
@ -313,11 +309,7 @@ size_t VendorInterface::Send(uint8_t type, const uint8_t* data, size_t length) {
ALOGV("%s: Sent wake before (%02x)", __func__, data[0] | (data[1] << 8));
}
int rv = write_safely(uart_fd_, &type, sizeof(type));
if (rv == sizeof(type))
rv = write_safely(uart_fd_, data, length);
return rv;
return hci_->Send(type, data, length);
}
void VendorInterface::OnFirmwareConfigured(uint8_t result) {
@ -357,26 +349,18 @@ void VendorInterface::OnTimeout() {
recent_activity_flag = false;
}
void VendorInterface::OnPacketReady() {
VendorInterface::get()->HandleIncomingPacket();
}
void VendorInterface::HandleIncomingEvent(const hidl_vec<uint8_t>& hci_packet) {
if (internal_command.cb != nullptr &&
internal_command_event_match(hci_packet)) {
HC_BT_HDR* bt_hdr = WrapPacketAndCopy(HCI_PACKET_TYPE_EVENT, hci_packet);
void VendorInterface::HandleIncomingPacket() {
HciPacketType hci_packet_type = hci_packetizer_.GetPacketType();
hidl_vec<uint8_t> hci_packet = hci_packetizer_.GetPacket();
if (internal_command.cb != nullptr &&
hci_packet_type == HCI_PACKET_TYPE_EVENT &&
internal_command_event_match(hci_packet)) {
HC_BT_HDR* bt_hdr =
WrapPacketAndCopy(HCI_PACKET_TYPE_EVENT, hci_packet);
// The callbacks can send new commands, so don't zero after calling.
tINT_CMD_CBACK saved_cb = internal_command.cb;
internal_command.cb = nullptr;
saved_cb(bt_hdr);
} else {
packet_read_cb_(hci_packet_type, hci_packet);
}
// The callbacks can send new commands, so don't zero after calling.
tINT_CMD_CBACK saved_cb = internal_command.cb;
internal_command.cb = nullptr;
saved_cb(bt_hdr);
} else {
event_cb_(hci_packet);
}
}
} // namespace implementation

View file

@ -20,7 +20,7 @@
#include "async_fd_watcher.h"
#include "bt_vendor_lib.h"
#include "hci_packetizer.h"
#include "hci_protocol.h"
namespace android {
namespace hardware {
@ -30,15 +30,15 @@ namespace implementation {
using ::android::hardware::hidl_vec;
using InitializeCompleteCallback = std::function<void(bool success)>;
using PacketReadCallback =
std::function<void(HciPacketType, const hidl_vec<uint8_t> &)>;
using PacketReadCallback = std::function<void(const hidl_vec<uint8_t>&)>;
class FirmwareStartupTimer;
class VendorInterface {
public:
static bool Initialize(InitializeCompleteCallback initialize_complete_cb,
PacketReadCallback packet_read_cb);
PacketReadCallback event_cb, PacketReadCallback acl_cb,
PacketReadCallback sco_cb);
static void Shutdown();
static VendorInterface *get();
@ -46,27 +46,25 @@ class VendorInterface {
void OnFirmwareConfigured(uint8_t result);
static void OnPacketReady();
private:
virtual ~VendorInterface() = default;
bool Open(InitializeCompleteCallback initialize_complete_cb,
PacketReadCallback packet_read_cb);
PacketReadCallback event_cb, PacketReadCallback acl_cb,
PacketReadCallback sco_cb);
void Close();
void OnTimeout();
void HandleIncomingPacket();
void HandleIncomingEvent(const hidl_vec<uint8_t>& hci_packet);
void *lib_handle_;
bt_vendor_interface_t *lib_interface_;
async::AsyncFdWatcher fd_watcher_;
int uart_fd_;
PacketReadCallback packet_read_cb_;
InitializeCompleteCallback initialize_complete_cb_;
hci::HciProtocol* hci_;
hci::HciPacketizer hci_packetizer_ {VendorInterface::OnPacketReady};
PacketReadCallback event_cb_;
FirmwareStartupTimer *firmware_startup_timer_;
};