Merge changes from topic "hci_aidl_hal"

* changes:
  Bluetooth HAL: Add an AIDL default implementation
  Start with async and HCI
  Bluetooth HIDL -> AIDL conversion
This commit is contained in:
Myles Watson
2022-11-02 23:47:01 +00:00
committed by Gerrit Code Review
26 changed files with 2304 additions and 0 deletions

28
bluetooth/aidl/Android.bp Normal file
View File

@@ -0,0 +1,28 @@
// This is the expected build file, but it may not be right in all cases
aidl_interface {
name: "android.hardware.bluetooth",
vendor_available: true,
host_supported: true,
srcs: ["android/hardware/bluetooth/*.aidl"],
stability: "vintf",
backend: {
cpp: {
// FIXME should this be disabled?
// prefer NDK backend which can be used anywhere
// If you disable this, you also need to delete the C++
// translate code.
enabled: true,
},
java: {
sdk_version: "module_current",
},
ndk: {
apex_available: [
"//apex_available:platform",
"com.android.btservices",
],
min_sdk_version: "33",
},
},
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.bluetooth;
@VintfStability
interface IBluetoothHci {
void close();
void initialize(in android.hardware.bluetooth.IBluetoothHciCallbacks callback);
void sendAclData(in byte[] data);
void sendHciCommand(in byte[] command);
void sendIsoData(in byte[] data);
void sendScoData(in byte[] data);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.bluetooth;
@VintfStability
interface IBluetoothHciCallbacks {
void aclDataReceived(in byte[] data);
void hciEventReceived(in byte[] event);
void initializationComplete(in android.hardware.bluetooth.Status status);
void isoDataReceived(in byte[] data);
void scoDataReceived(in byte[] data);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.bluetooth;
@Backing(type="int") @VintfStability
enum Status {
SUCCESS = 0,
ALREADY_INITIALIZED = 1,
UNABLE_TO_OPEN_INTERFACE = 2,
HARDWARE_INITIALIZATION_ERROR = 3,
UNKNOWN = 4,
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2022 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.
*/
package android.hardware.bluetooth;
import android.hardware.bluetooth.IBluetoothHciCallbacks;
/**
* The Host Controller Interface (HCI) is the layer defined by the Bluetooth
* specification between the software that runs on the host and the Bluetooth
* controller chip. This boundary is the natural choice for a Hardware
* Abstraction Layer (HAL). Dealing only in HCI packets and events simplifies
* the stack and abstracts away power management, initialization, and other
* implementation-specific details related to the hardware.
*/
@VintfStability
interface IBluetoothHci {
/**
* Close the HCI interface
*/
void close();
/**
* Initialize the Bluetooth interface and set the callbacks.
*/
void initialize(in IBluetoothHciCallbacks callback);
/**
* Send an HCI ACL data packet (as specified in the Bluetooth Specification
* V4.2, Vol 2, Part 5, Section 5.4.2) to the Bluetooth controller.
* Packets must be processed in order.
* @param data HCI data packet to be sent
*/
void sendAclData(in byte[] data);
/**
* Send an HCI command (as specified in the Bluetooth Specification
* V4.2, Vol 2, Part 5, Section 5.4.1) to the Bluetooth controller.
* Commands must be executed in order.
*
* @param command is the HCI command to be sent
*/
void sendHciCommand(in byte[] command);
/**
* Send an ISO data packet (as specified in the Bluetooth Core
* Specification v5.2) to the Bluetooth controller.
* Packets must be processed in order.
* @param data HCI data packet to be sent
*/
void sendIsoData(in byte[] data);
/**
* Send an SCO data packet (as specified in the Bluetooth Specification
* V4.2, Vol 2, Part 5, Section 5.4.3) to the Bluetooth controller.
* Packets must be processed in order.
* @param data HCI data packet to be sent
*/
void sendScoData(in byte[] data);
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2022 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.
*/
package android.hardware.bluetooth;
import android.hardware.bluetooth.Status;
/**
* The interface from the Bluetooth Controller to the stack.
*/
@VintfStability
interface IBluetoothHciCallbacks {
/**
* Send an ACL data packet from the controller to the host.
* @param data the ACL HCI packet to be passed to the host stack
*/
void aclDataReceived(in byte[] data);
/**
* This function is invoked when an HCI event is received from the
* Bluetooth controller to be forwarded to the Bluetooth stack.
* @param event is the HCI event to be sent to the Bluetooth stack.
*/
void hciEventReceived(in byte[] event);
/**
* Invoked when the Bluetooth controller initialization has been
* completed.
*/
void initializationComplete(in Status status);
/**
* Send a ISO data packet from the controller to the host.
* @param data the ISO HCI packet to be passed to the host stack
*/
void isoDataReceived(in byte[] data);
/**
* Send a SCO data packet from the controller to the host.
* @param data the SCO HCI packet to be passed to the host stack
*/
void scoDataReceived(in byte[] data);
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2022 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.
*/
package android.hardware.bluetooth;
@VintfStability
@Backing(type="int")
enum Status {
SUCCESS,
ALREADY_INITIALIZED,
UNABLE_TO_OPEN_INTERFACE,
HARDWARE_INITIALIZATION_ERROR,
UNKNOWN,
}

View File

@@ -0,0 +1,79 @@
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_defaults {
name: "android.hardware.bluetooth-service-build-defaults",
cflags: [
"-Wall",
"-Wextra",
],
shared_libs: [
"android.hardware.bluetooth-V1-ndk",
"libbase",
"libbinder_ndk",
"libcutils",
"libhidlbase",
"liblog",
"libutils",
],
static_libs: [
"android.hardware.bluetooth.async",
"android.hardware.bluetooth.hci",
],
}
cc_library_static {
name: "libbluetoothhcihalimpl",
vendor_available: true,
host_supported: true,
defaults: ["android.hardware.bluetooth-service-build-defaults"],
srcs: [
"BluetoothHci.cpp",
],
}
cc_binary {
name: "android.hardware.bluetooth-service.default",
relative_install_path: "hw",
init_rc: ["bluetooth-service-default.rc"],
vintf_fragments: ["bluetooth-service-default.xml"],
vendor: true,
defaults: ["android.hardware.bluetooth-service-build-defaults"],
srcs: [
"service.cpp",
],
shared_libs: [
"android.hardware.bluetooth-V1-ndk",
"libbase",
"libbinder_ndk",
"libhidlbase",
"libutils",
"liblog",
],
static_libs: [
"libbluetoothhcihalimpl",
],
}
cc_fuzz {
name: "android.hardware.bluetooth-service.default_fuzzer",
host_supported: true,
defaults: ["service_fuzzer_defaults"],
srcs: [
"test/fuzzer.cpp",
],
static_libs: [
"android.hardware.bluetooth.async",
"android.hardware.bluetooth.hci",
"android.hardware.bluetooth-V1-ndk",
"libbluetoothhcihalimpl",
"liblog",
],
fuzz_config: {
componentid: 27441,
cc: [
"mylesgw@google.com",
],
},
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2022 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 "android.hardware.bluetooth.service.default"
#include "BluetoothHci.h"
#include <cutils/properties.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <string.h>
#include <sys/uio.h>
#include <termios.h>
#include "log/log.h"
namespace {
int SetTerminalRaw(int fd) {
termios terminal_settings;
int rval = tcgetattr(fd, &terminal_settings);
if (rval < 0) {
return rval;
}
cfmakeraw(&terminal_settings);
rval = tcsetattr(fd, TCSANOW, &terminal_settings);
return rval;
}
} // namespace
using namespace ::android::hardware::bluetooth::hci;
using namespace ::android::hardware::bluetooth::async;
namespace aidl::android::hardware::bluetooth::impl {
void OnDeath(void* cookie);
class BluetoothDeathRecipient {
public:
BluetoothDeathRecipient(BluetoothHci* hci) : mHci(hci) {}
void LinkToDeath(const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
mCb = cb;
clientDeathRecipient_ = AIBinder_DeathRecipient_new(OnDeath);
auto linkToDeathReturnStatus = AIBinder_linkToDeath(
mCb->asBinder().get(), clientDeathRecipient_, this /* cookie */);
LOG_ALWAYS_FATAL_IF(linkToDeathReturnStatus != STATUS_OK,
"Unable to link to death recipient");
}
void UnlinkToDeath(const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
LOG_ALWAYS_FATAL_IF(cb != mCb, "Unable to unlink mismatched pointers");
}
void serviceDied() {
if (mCb != nullptr && !AIBinder_isAlive(mCb->asBinder().get())) {
ALOGE("Bluetooth remote service has died");
} else {
ALOGE("BluetoothDeathRecipient::serviceDied called but service not dead");
return;
}
has_died_ = true;
mHci->close();
}
BluetoothHci* mHci;
std::shared_ptr<IBluetoothHciCallbacks> mCb;
AIBinder_DeathRecipient* clientDeathRecipient_;
bool getHasDied() const { return has_died_; }
private:
bool has_died_;
};
void OnDeath(void* cookie) {
auto* death_recipient = static_cast<BluetoothDeathRecipient*>(cookie);
death_recipient->serviceDied();
}
BluetoothHci::BluetoothHci(const std::string& dev_path) {
char property_bytes[PROPERTY_VALUE_MAX];
property_get("vendor.ser.bt-uart", property_bytes, dev_path.c_str());
mDevPath = std::string(property_bytes);
mDeathRecipient = std::make_shared<BluetoothDeathRecipient>(this);
}
ndk::ScopedAStatus BluetoothHci::initialize(
const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
ALOGI(__func__);
mFd = open(mDevPath.c_str(), O_RDWR);
if (mFd < 0) {
ALOGE("Could not connect to bt: %s (%s)", mDevPath.c_str(),
strerror(errno));
return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
}
if (int ret = SetTerminalRaw(mFd) < 0) {
ALOGE("Could not make %s a raw terminal %d(%s)", mDevPath.c_str(), ret,
strerror(errno));
return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
}
mCb = cb;
if (mCb == nullptr) {
ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
}
mDeathRecipient->LinkToDeath(mCb);
auto init_ret = cb->initializationComplete(Status::SUCCESS);
if (!init_ret.isOk()) {
if (!mDeathRecipient->getHasDied()) {
ALOGE("Error sending init callback, but no death notification.");
}
return ndk::ScopedAStatus::fromServiceSpecificError(
STATUS_FAILED_TRANSACTION);
}
mH4 = std::make_shared<H4Protocol>(
mFd,
[](const std::vector<uint8_t>& /* raw_command */) {
LOG_ALWAYS_FATAL("Unexpected command!");
},
[this](const std::vector<uint8_t>& raw_event) {
mCb->hciEventReceived(raw_event);
},
[this](const std::vector<uint8_t>& raw_acl) {
mCb->hciEventReceived(raw_acl);
},
[this](const std::vector<uint8_t>& raw_sco) {
mCb->hciEventReceived(raw_sco);
},
[this](const std::vector<uint8_t>& raw_iso) {
mCb->hciEventReceived(raw_iso);
},
[this]() {
ALOGI("HCI socket device disconnected");
mFdWatcher.StopWatchingFileDescriptors();
});
mFdWatcher.WatchFdForNonBlockingReads(mFd,
[this](int) { mH4->OnDataReady(); });
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus BluetoothHci::close() {
ALOGI(__func__);
mFdWatcher.StopWatchingFileDescriptors();
::close(mFd);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus BluetoothHci::sendHciCommand(
const std::vector<uint8_t>& packet) {
send(PacketType::COMMAND, packet);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus BluetoothHci::sendAclData(
const std::vector<uint8_t>& packet) {
send(PacketType::ACL_DATA, packet);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus BluetoothHci::sendScoData(
const std::vector<uint8_t>& packet) {
send(PacketType::SCO_DATA, packet);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus BluetoothHci::sendIsoData(
const std::vector<uint8_t>& packet) {
send(PacketType::ISO_DATA, packet);
return ndk::ScopedAStatus::ok();
}
void BluetoothHci::send(PacketType type, const std::vector<uint8_t>& v) {
mH4->Send(type, v);
}
} // namespace aidl::android::hardware::bluetooth::impl

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2022 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 <aidl/android/hardware/bluetooth/BnBluetoothHci.h>
#include <aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.h>
#include <log/log.h>
#include <string>
#include "async_fd_watcher.h"
#include "h4_protocol.h"
namespace aidl::android::hardware::bluetooth::impl {
class BluetoothDeathRecipient;
// This Bluetooth HAL implementation connects with a serial port at dev_path_.
class BluetoothHci : public BnBluetoothHci {
public:
BluetoothHci(const std::string& dev_path = "/dev/hvc5");
ndk::ScopedAStatus initialize(
const std::shared_ptr<IBluetoothHciCallbacks>& cb) override;
ndk::ScopedAStatus sendHciCommand(
const std::vector<uint8_t>& packet) override;
ndk::ScopedAStatus sendAclData(const std::vector<uint8_t>& packet) override;
ndk::ScopedAStatus sendScoData(const std::vector<uint8_t>& packet) override;
ndk::ScopedAStatus sendIsoData(const std::vector<uint8_t>& packet) override;
ndk::ScopedAStatus close() override;
static void OnPacketReady();
static BluetoothHci* get();
private:
int mFd{-1};
std::shared_ptr<IBluetoothHciCallbacks> mCb = nullptr;
std::shared_ptr<::android::hardware::bluetooth::hci::H4Protocol> mH4;
std::shared_ptr<BluetoothDeathRecipient> mDeathRecipient;
std::string mDevPath;
::android::hardware::bluetooth::async::AsyncFdWatcher mFdWatcher;
void send(::android::hardware::bluetooth::hci::PacketType type,
const std::vector<uint8_t>& packet);
};
} // namespace aidl::android::hardware::bluetooth::impl

View File

@@ -0,0 +1,6 @@
service bluetooth_hal_service /vendor/bin/hw/android.hardware.bluetooth-service.default
class hal
capabilities BLOCK_SUSPEND NET_ADMIN SYS_NICE
user bluetooth
group bluetooth
task_profiles HighPerformance

View File

@@ -0,0 +1,6 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.bluetooth</name>
<fqname>IBluetoothHci/default</fqname>
</hal>
</manifest>

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2022 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 "aidl.android.hardware.bluetooth.service.default"
#include <aidl/android/hardware/bluetooth/IBluetoothHci.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include "BluetoothHci.h"
using ::aidl::android::hardware::bluetooth::impl::BluetoothHci;
using ::android::hardware::configureRpcThreadpool;
using ::android::hardware::joinRpcThreadpool;
int main(int /* argc */, char** /* argv */) {
ALOGI("Bluetooth HAL starting");
if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
ALOGI("failed to set thread pool max thread count");
return 1;
}
std::shared_ptr<BluetoothHci> service =
ndk::SharedRefBase::make<BluetoothHci>();
std::string instance = std::string() + BluetoothHci::descriptor + "/default";
auto result =
AServiceManager_addService(service->asBinder().get(), instance.c_str());
if (result == STATUS_OK) {
ABinderProcess_joinThreadPool();
} else {
ALOGE("Could not register as a service!");
}
return 0;
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2022 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 <fuzzbinder/libbinder_ndk_driver.h>
#include <fuzzer/FuzzedDataProvider.h>
#include "BluetoothHci.h"
using aidl::android::hardware::bluetooth::impl::BluetoothHci;
using android::fuzzService;
using ndk::SharedRefBase;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
auto service = SharedRefBase::make<BluetoothHci>();
fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
return 0;
}

View File

@@ -0,0 +1,41 @@
package {
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_library_static {
name: "android.hardware.bluetooth.async",
vendor_available: true,
host_supported: true,
srcs: [
"async_fd_watcher.cc",
],
export_include_dirs: ["."],
shared_libs: [
"liblog",
],
cflags: [
"-Wall",
"-Werror",
],
}
cc_test {
name: "bluetooth-vendor-interface-async-test",
host_supported: true,
srcs: [
"test/async_fd_watcher_unittest.cc",
],
shared_libs: [
"liblog",
"libutils",
],
static_libs: [
"android.hardware.bluetooth.async",
"libgmock",
],
cflags: [
"-Wall",
"-Werror",
],
test_suites: ["general-tests"],
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2022 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 "async_fd_watcher.h"
#include <string.h>
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <map>
#include <mutex>
#include <thread>
#include <vector>
#include "fcntl.h"
#include "log/log.h"
#include "sys/select.h"
#include "unistd.h"
static const int INVALID_FD = -1;
namespace android::hardware::bluetooth::async {
int AsyncFdWatcher::WatchFdForNonBlockingReads(
int file_descriptor, const ReadCallback& on_read_fd_ready_callback) {
// Add file descriptor and callback
{
std::unique_lock<std::mutex> guard(internal_mutex_);
watched_fds_[file_descriptor] = on_read_fd_ready_callback;
}
// Start the thread if not started yet
return tryStartThread();
}
int AsyncFdWatcher::ConfigureTimeout(
const std::chrono::milliseconds timeout,
const TimeoutCallback& on_timeout_callback) {
// Add timeout and callback
{
std::unique_lock<std::mutex> guard(timeout_mutex_);
timeout_cb_ = on_timeout_callback;
timeout_ms_ = timeout;
}
notifyThread();
return 0;
}
void AsyncFdWatcher::StopWatchingFileDescriptors() { stopThread(); }
AsyncFdWatcher::~AsyncFdWatcher() {}
// Make sure to call this with at least one file descriptor ready to be
// watched upon or the thread routine will return immediately
int AsyncFdWatcher::tryStartThread() {
if (std::atomic_exchange(&running_, true)) return 0;
// Set up the communication channel
int pipe_fds[2];
if (pipe2(pipe_fds, O_NONBLOCK)) return -1;
notification_listen_fd_ = pipe_fds[0];
notification_write_fd_ = pipe_fds[1];
thread_ = std::thread([this]() { ThreadRoutine(); });
if (!thread_.joinable()) return -1;
return 0;
}
int AsyncFdWatcher::stopThread() {
if (!std::atomic_exchange(&running_, false)) return 0;
notifyThread();
if (std::this_thread::get_id() != thread_.get_id()) {
thread_.join();
}
{
std::unique_lock<std::mutex> guard(internal_mutex_);
watched_fds_.clear();
}
{
std::unique_lock<std::mutex> guard(timeout_mutex_);
timeout_cb_ = nullptr;
}
close(notification_listen_fd_);
close(notification_write_fd_);
return 0;
}
int AsyncFdWatcher::notifyThread() {
uint8_t buffer[] = {0};
if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
return -1;
}
return 0;
}
void AsyncFdWatcher::ThreadRoutine() {
while (running_) {
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(notification_listen_fd_, &read_fds);
int max_read_fd = INVALID_FD;
for (auto& it : watched_fds_) {
FD_SET(it.first, &read_fds);
max_read_fd = std::max(max_read_fd, it.first);
}
struct timeval timeout;
struct timeval* timeout_ptr = NULL;
if (timeout_ms_ > std::chrono::milliseconds(0)) {
timeout.tv_sec = timeout_ms_.count() / 1000;
timeout.tv_usec = (timeout_ms_.count() % 1000) * 1000;
timeout_ptr = &timeout;
}
// Wait until there is data available to read on some FD.
int nfds = std::max(notification_listen_fd_, max_read_fd);
int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
// There was some error.
if (retval < 0) continue;
// Timeout.
if (retval == 0) {
// Allow the timeout callback to modify the timeout.
TimeoutCallback saved_cb;
{
std::unique_lock<std::mutex> guard(timeout_mutex_);
if (timeout_ms_ > std::chrono::milliseconds(0)) saved_cb = timeout_cb_;
}
if (saved_cb != nullptr) saved_cb();
continue;
}
// Read data from the notification FD.
if (FD_ISSET(notification_listen_fd_, &read_fds)) {
char buffer[] = {0};
TEMP_FAILURE_RETRY(read(notification_listen_fd_, buffer, 1));
continue;
}
// Invoke the data ready callbacks if appropriate.
{
// Hold the mutex to make sure that the callbacks are still valid.
std::unique_lock<std::mutex> guard(internal_mutex_);
for (auto& it : watched_fds_) {
if (FD_ISSET(it.first, &read_fds)) {
it.second(it.first);
}
}
}
}
}
} // namespace android::hardware::bluetooth::async

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2022 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 <map>
#include <mutex>
#include <thread>
namespace android::hardware::bluetooth::async {
using ReadCallback = std::function<void(int)>;
using TimeoutCallback = std::function<void(void)>;
class AsyncFdWatcher {
public:
AsyncFdWatcher() = default;
~AsyncFdWatcher();
int WatchFdForNonBlockingReads(int file_descriptor,
const ReadCallback& on_read_fd_ready_callback);
int ConfigureTimeout(const std::chrono::milliseconds timeout,
const TimeoutCallback& on_timeout_callback);
void StopWatchingFileDescriptors();
private:
AsyncFdWatcher(const AsyncFdWatcher&) = delete;
AsyncFdWatcher& operator=(const AsyncFdWatcher&) = delete;
int tryStartThread();
int stopThread();
int notifyThread();
void ThreadRoutine();
std::atomic_bool running_{false};
std::thread thread_;
std::mutex internal_mutex_;
std::mutex timeout_mutex_;
std::map<int, ReadCallback> watched_fds_;
int notification_listen_fd_;
int notification_write_fd_;
TimeoutCallback timeout_cb_;
std::chrono::milliseconds timeout_ms_;
};
} // namespace android::hardware::bluetooth::async

View File

@@ -0,0 +1,377 @@
/*
* Copyright 2022 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 "async_fd_watcher_unittest"
#include "async_fd_watcher.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdint>
#include <cstring>
#include <vector>
namespace android::hardware::bluetooth::async_test {
using android::hardware::bluetooth::async::AsyncFdWatcher;
class AsyncFdWatcherSocketTest : public ::testing::Test {
public:
static const uint16_t kPort = 6111;
static const size_t kBufferSize = 16;
bool CheckBufferEquals() {
return strcmp(server_buffer_, client_buffer_) == 0;
}
protected:
int StartServer() {
ALOGD("%s", __func__);
struct sockaddr_in serv_addr;
int fd = socket(AF_INET, SOCK_STREAM, 0);
EXPECT_FALSE(fd < 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
serv_addr.sin_port = htons(kPort);
int reuse_flag = 1;
EXPECT_FALSE(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_flag,
sizeof(reuse_flag)) < 0);
EXPECT_FALSE(bind(fd, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0);
ALOGD("%s before listen", __func__);
listen(fd, 1);
return fd;
}
int AcceptConnection(int fd) {
ALOGD("%s", __func__);
struct sockaddr_in cli_addr;
memset(&cli_addr, 0, sizeof(cli_addr));
socklen_t clilen = sizeof(cli_addr);
int connection_fd = accept(fd, (struct sockaddr*)&cli_addr, &clilen);
EXPECT_FALSE(connection_fd < 0);
return connection_fd;
}
void ReadIncomingMessage(int fd) {
ALOGD("%s", __func__);
int n = TEMP_FAILURE_RETRY(read(fd, server_buffer_, kBufferSize - 1));
EXPECT_FALSE(n < 0);
if (n == 0) { // got EOF
ALOGD("%s: EOF", __func__);
} else {
ALOGD("%s: Got something", __func__);
n = write(fd, "1", 1);
}
}
void SetUp() override {
ALOGD("%s", __func__);
memset(server_buffer_, 0, kBufferSize);
memset(client_buffer_, 0, kBufferSize);
}
void ConfigureServer() {
socket_fd_ = StartServer();
conn_watcher_.WatchFdForNonBlockingReads(socket_fd_, [this](int fd) {
int connection_fd = AcceptConnection(fd);
ALOGD("%s: Conn_watcher fd = %d", __func__, fd);
conn_watcher_.ConfigureTimeout(std::chrono::seconds(0), []() {
bool connection_timeout_cleared = false;
ASSERT_TRUE(connection_timeout_cleared);
});
ALOGD("%s: 3", __func__);
async_fd_watcher_.WatchFdForNonBlockingReads(
connection_fd, [this](int fd) { ReadIncomingMessage(fd); });
// Time out if it takes longer than a second.
SetTimeout(std::chrono::seconds(1));
});
conn_watcher_.ConfigureTimeout(std::chrono::seconds(1), []() {
bool connection_timeout = true;
ASSERT_FALSE(connection_timeout);
});
}
void CleanUpServer() {
async_fd_watcher_.StopWatchingFileDescriptors();
conn_watcher_.StopWatchingFileDescriptors();
close(socket_fd_);
}
void TearDown() override {
ALOGD("%s 3", __func__);
EXPECT_TRUE(CheckBufferEquals());
}
void OnTimeout() {
ALOGD("%s", __func__);
timed_out_ = true;
}
void ClearTimeout() {
ALOGD("%s", __func__);
timed_out_ = false;
}
bool TimedOut() {
ALOGD("%s %d", __func__, timed_out_ ? 1 : 0);
return timed_out_;
}
void SetTimeout(std::chrono::milliseconds timeout_ms) {
ALOGD("%s", __func__);
async_fd_watcher_.ConfigureTimeout(timeout_ms, [this]() { OnTimeout(); });
ClearTimeout();
}
int ConnectClient() {
ALOGD("%s", __func__);
int socket_cli_fd = socket(AF_INET, SOCK_STREAM, 0);
EXPECT_FALSE(socket_cli_fd < 0);
struct sockaddr_in serv_addr;
memset((void*)&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
serv_addr.sin_port = htons(kPort);
int result =
connect(socket_cli_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
EXPECT_FALSE(result < 0);
return socket_cli_fd;
}
void WriteFromClient(int socket_cli_fd) {
ALOGD("%s", __func__);
strcpy(client_buffer_, "1");
int n = write(socket_cli_fd, client_buffer_, strlen(client_buffer_));
EXPECT_TRUE(n > 0);
}
void AwaitServerResponse(int socket_cli_fd) {
ALOGD("%s", __func__);
int n = read(socket_cli_fd, client_buffer_, 1);
ALOGD("%s done", __func__);
EXPECT_TRUE(n > 0);
}
private:
AsyncFdWatcher async_fd_watcher_;
AsyncFdWatcher conn_watcher_;
int socket_fd_;
char server_buffer_[kBufferSize];
char client_buffer_[kBufferSize];
bool timed_out_;
};
// Use a single AsyncFdWatcher to signal a connection to the server socket.
TEST_F(AsyncFdWatcherSocketTest, Connect) {
int socket_fd = StartServer();
AsyncFdWatcher conn_watcher;
conn_watcher.WatchFdForNonBlockingReads(socket_fd, [this](int fd) {
int connection_fd = AcceptConnection(fd);
close(connection_fd);
});
// Fail if the client doesn't connect within 1 second.
conn_watcher.ConfigureTimeout(std::chrono::seconds(1), []() {
bool connection_timeout = true;
ASSERT_FALSE(connection_timeout);
});
int socket_cli_fd = ConnectClient();
conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
close(socket_cli_fd);
}
// Use a single AsyncFdWatcher to signal a connection to the server socket.
TEST_F(AsyncFdWatcherSocketTest, TimedOutConnect) {
int socket_fd = StartServer();
bool timed_out = false;
bool* timeout_ptr = &timed_out;
AsyncFdWatcher conn_watcher;
conn_watcher.WatchFdForNonBlockingReads(socket_fd, [this](int fd) {
int connection_fd = AcceptConnection(fd);
close(connection_fd);
});
// Set the timeout flag after 100ms.
conn_watcher.ConfigureTimeout(std::chrono::milliseconds(100),
[timeout_ptr]() { *timeout_ptr = true; });
EXPECT_FALSE(timed_out);
sleep(1);
EXPECT_TRUE(timed_out);
conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
}
// Modify the timeout in a timeout callback.
TEST_F(AsyncFdWatcherSocketTest, TimedOutSchedulesTimeout) {
int socket_fd = StartServer();
bool timed_out = false;
bool timed_out2 = false;
AsyncFdWatcher conn_watcher;
conn_watcher.WatchFdForNonBlockingReads(socket_fd, [this](int fd) {
int connection_fd = AcceptConnection(fd);
close(connection_fd);
});
// Set a timeout flag in each callback.
conn_watcher.ConfigureTimeout(std::chrono::milliseconds(500),
[&conn_watcher, &timed_out, &timed_out2]() {
timed_out = true;
conn_watcher.ConfigureTimeout(
std::chrono::seconds(1),
[&timed_out2]() { timed_out2 = true; });
});
EXPECT_FALSE(timed_out);
EXPECT_FALSE(timed_out2);
sleep(1);
EXPECT_TRUE(timed_out);
EXPECT_FALSE(timed_out2);
sleep(1);
EXPECT_TRUE(timed_out);
EXPECT_TRUE(timed_out2);
conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
}
MATCHER_P(ReadAndMatchSingleChar, byte,
"Reads a byte from the file descriptor and matches the value against "
"byte") {
char inbuf[1] = {0};
int n = TEMP_FAILURE_RETRY(read(arg, inbuf, 1));
TEMP_FAILURE_RETRY(write(arg, inbuf, 1));
if (n != 1) {
return false;
}
return inbuf[0] == byte;
};
// Use a single AsyncFdWatcher to watch two file descriptors.
TEST_F(AsyncFdWatcherSocketTest, WatchTwoFileDescriptors) {
int sockfd1[2];
int sockfd2[2];
socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd1);
socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd2);
testing::MockFunction<void(int)> cb1;
testing::MockFunction<void(int)> cb2;
AsyncFdWatcher watcher;
watcher.WatchFdForNonBlockingReads(sockfd1[0], cb1.AsStdFunction());
watcher.WatchFdForNonBlockingReads(sockfd2[0], cb2.AsStdFunction());
EXPECT_CALL(cb1, Call(ReadAndMatchSingleChar('1')));
char one_buf[1] = {'1'};
TEMP_FAILURE_RETRY(write(sockfd1[1], one_buf, sizeof(one_buf)));
EXPECT_CALL(cb2, Call(ReadAndMatchSingleChar('2')));
char two_buf[1] = {'2'};
TEMP_FAILURE_RETRY(write(sockfd2[1], two_buf, sizeof(two_buf)));
// Blocking read instead of a flush.
TEMP_FAILURE_RETRY(read(sockfd1[1], one_buf, sizeof(one_buf)));
TEMP_FAILURE_RETRY(read(sockfd2[1], two_buf, sizeof(two_buf)));
watcher.StopWatchingFileDescriptors();
}
// Use two AsyncFdWatchers to set up a server socket.
TEST_F(AsyncFdWatcherSocketTest, ClientServer) {
ConfigureServer();
int socket_cli_fd = ConnectClient();
WriteFromClient(socket_cli_fd);
AwaitServerResponse(socket_cli_fd);
close(socket_cli_fd);
CleanUpServer();
}
// Use two AsyncFdWatchers to set up a server socket, which times out.
TEST_F(AsyncFdWatcherSocketTest, TimeOutTest) {
ConfigureServer();
int socket_cli_fd = ConnectClient();
while (!TimedOut()) sleep(1);
close(socket_cli_fd);
CleanUpServer();
}
// Use two AsyncFdWatchers to set up a server socket, which times out.
TEST_F(AsyncFdWatcherSocketTest, RepeatedTimeOutTest) {
ConfigureServer();
int socket_cli_fd = ConnectClient();
ClearTimeout();
// Time out when there are no writes.
EXPECT_FALSE(TimedOut());
sleep(2);
EXPECT_TRUE(TimedOut());
ClearTimeout();
// Don't time out when there is a write.
WriteFromClient(socket_cli_fd);
AwaitServerResponse(socket_cli_fd);
EXPECT_FALSE(TimedOut());
ClearTimeout();
// Time out when the write is late.
sleep(2);
WriteFromClient(socket_cli_fd);
AwaitServerResponse(socket_cli_fd);
EXPECT_TRUE(TimedOut());
ClearTimeout();
// Time out when there is a pause after a write.
WriteFromClient(socket_cli_fd);
sleep(2);
AwaitServerResponse(socket_cli_fd);
EXPECT_TRUE(TimedOut());
ClearTimeout();
close(socket_cli_fd);
CleanUpServer();
}
} // namespace android::hardware::bluetooth::async_test

46
bluetooth/hci/Android.bp Normal file
View File

@@ -0,0 +1,46 @@
package {
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_library_static {
name: "android.hardware.bluetooth.hci",
vendor_available: true,
host_supported: true,
defaults: ["hidl_defaults"],
srcs: [
"hci_packetizer.cc",
"h4_protocol.cc",
],
export_include_dirs: ["."],
shared_libs: [
"libbase",
"libhidlbase",
"liblog",
"libutils",
],
}
cc_test {
name: "bluetooth-vendor-interface-hci-test",
host_supported: true,
defaults: ["hidl_defaults"],
srcs: [
"test/h4_protocol_unittest.cc",
],
shared_libs: [
"libbase",
"libhidlbase",
"liblog",
"libutils",
],
static_libs: [
"android.hardware.bluetooth.async",
"android.hardware.bluetooth.hci",
"libgmock",
],
sanitize: {
address: true,
cfi: true,
},
test_suites: ["general-tests"],
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2022 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 <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/uio.h>
#include "log/log.h"
namespace android::hardware::bluetooth::hci {
H4Protocol::H4Protocol(int fd, PacketReadCallback cmd_cb,
PacketReadCallback acl_cb, PacketReadCallback sco_cb,
PacketReadCallback event_cb, PacketReadCallback iso_cb,
DisconnectCallback disconnect_cb)
: uart_fd_(fd),
cmd_cb_(std::move(cmd_cb)),
acl_cb_(std::move(acl_cb)),
sco_cb_(std::move(sco_cb)),
event_cb_(std::move(event_cb)),
iso_cb_(std::move(iso_cb)),
disconnect_cb_(std::move(disconnect_cb)) {}
size_t H4Protocol::Send(PacketType type, const std::vector<uint8_t>& vector) {
return Send(type, vector.data(), vector.size());
}
size_t H4Protocol::Send(PacketType type, const uint8_t* data, size_t length) {
/* For HCI communication over USB dongle, multiple write results in
* response timeout as driver expect type + data at once to process
* the command, so using "writev"(for atomicity) here.
*/
struct iovec iov[2];
ssize_t ret = 0;
iov[0].iov_base = &type;
iov[0].iov_len = sizeof(type);
iov[1].iov_base = (void*)data;
iov[1].iov_len = length;
while (1) {
ret = TEMP_FAILURE_RETRY(writev(uart_fd_, iov, 2));
if (ret == -1) {
if (errno == EAGAIN) {
ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
continue;
}
} else if (ret == 0) {
// Nothing written :(
ALOGE("%s zero bytes written - something went wrong...", __func__);
break;
}
break;
}
return ret;
}
size_t H4Protocol::OnPacketReady(const std::vector<uint8_t>& packet) {
switch (hci_packet_type_) {
case PacketType::COMMAND:
cmd_cb_(packet);
break;
case PacketType::ACL_DATA:
acl_cb_(packet);
break;
case PacketType::SCO_DATA:
sco_cb_(packet);
break;
case PacketType::EVENT:
event_cb_(packet);
break;
case PacketType::ISO_DATA:
iso_cb_(packet);
break;
default: {
LOG_ALWAYS_FATAL("Bad packet type 0x%x",
static_cast<int>(hci_packet_type_));
}
}
return packet.size();
}
void H4Protocol::SendDataToPacketizer(uint8_t* buffer, size_t length) {
std::vector<uint8_t> input_buffer{buffer, buffer + length};
size_t buffer_offset = 0;
while (buffer_offset < input_buffer.size()) {
if (hci_packet_type_ == PacketType::UNKNOWN) {
hci_packet_type_ =
static_cast<PacketType>(input_buffer.data()[buffer_offset]);
buffer_offset += 1;
} else {
bool packet_ready = hci_packetizer_.OnDataReady(
hci_packet_type_, input_buffer, buffer_offset);
if (packet_ready) {
// Call packet callback and move offset.
buffer_offset += OnPacketReady(hci_packetizer_.GetPacket());
// Get ready for the next type byte.
hci_packet_type_ = PacketType::UNKNOWN;
} else {
// The data was consumed, but there wasn't a packet.
buffer_offset = input_buffer.size();
}
}
}
}
void H4Protocol::OnDataReady() {
if (disconnected_) {
return;
}
uint8_t buffer[kMaxPacketLength];
ssize_t bytes_read =
TEMP_FAILURE_RETRY(read(uart_fd_, buffer, kMaxPacketLength));
if (bytes_read == 0) {
ALOGI("No bytes read, calling the disconnect callback");
disconnected_ = true;
disconnect_cb_();
return;
}
if (bytes_read < 0) {
ALOGW("error reading from UART (%s)", strerror(errno));
return;
}
SendDataToPacketizer(buffer, bytes_read);
}
} // namespace android::hardware::bluetooth::hci

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2022 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 <memory>
#include <vector>
#include "hci_internals.h"
#include "hci_packetizer.h"
namespace android::hardware::bluetooth::hci {
using PacketReadCallback = std::function<void(const std::vector<uint8_t>&)>;
using DisconnectCallback = std::function<void(void)>;
class H4Protocol {
public:
H4Protocol(int fd, PacketReadCallback cmd_cb, PacketReadCallback acl_cb,
PacketReadCallback sco_cb, PacketReadCallback event_cb,
PacketReadCallback iso_cb, DisconnectCallback disconnect_cb);
size_t Send(PacketType type, const uint8_t* data, size_t length);
size_t Send(PacketType type, const std::vector<uint8_t>& data);
void OnDataReady();
protected:
size_t OnPacketReady(const std::vector<uint8_t>& packet);
void SendDataToPacketizer(uint8_t* buffer, size_t length);
private:
int uart_fd_;
bool disconnected_{false};
PacketReadCallback cmd_cb_;
PacketReadCallback acl_cb_;
PacketReadCallback sco_cb_;
PacketReadCallback event_cb_;
PacketReadCallback iso_cb_;
DisconnectCallback disconnect_cb_;
PacketType hci_packet_type_{PacketType::UNKNOWN};
HciPacketizer hci_packetizer_;
/**
* Question : Why read in single chunk rather than multiple reads?
* Answer: Using multiple reads does not work with some BT USB dongles.
* Reading in single shot gives expected response.
* ACL max length is 2 bytes, so using 64K as the buffer length.
*/
static constexpr size_t kMaxPacketLength = 64 * 1024;
};
} // namespace android::hardware::bluetooth::hci

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2022 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 <cstdlib>
namespace android::hardware::bluetooth::hci {
// HCI UART transport packet types (Volume 4, Part A, 2)
enum class PacketType : uint8_t {
UNKNOWN = 0,
COMMAND = 1,
ACL_DATA = 2,
SCO_DATA = 3,
EVENT = 4,
ISO_DATA = 5,
};
// 2 bytes for opcode, 1 byte for parameter length (Volume 4, Part E, 5.4.1)
static constexpr size_t kCommandHeaderSize = 3;
static constexpr size_t kCommandLengthOffset = 2;
// 2 bytes for handle, 2 bytes for data length (Volume 4, Part E, 5.4.2)
static constexpr size_t kAclHeaderSize = 4;
static constexpr size_t kAclLengthOffset = 2;
// 2 bytes for handle, 1 byte for data length (Volume 4, Part E, 5.4.3)
static constexpr size_t kScoHeaderSize = 3;
static constexpr size_t kScoLengthOffset = 2;
// 1 byte for event code, 1 byte for parameter length (Volume 4, Part E, 5.4.4)
static constexpr size_t kEventHeaderSize = 2;
static constexpr size_t kEventLengthOffset = 1;
// 2 bytes for handle, 2 bytes for data length (Volume 4, Part E, 5.4.5)
static constexpr size_t kIsoHeaderSize = 4;
static constexpr size_t kIsoLengthOffset = 2;
static constexpr size_t kMaxHeaderSize = kAclHeaderSize;
} // namespace android::hardware::bluetooth::hci

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2022 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_packetizer.h"
#define LOG_TAG "android.hardware.bluetooth.hci-packetizer"
#include "log/log.h"
namespace android::hardware::bluetooth::hci {
namespace {
const size_t header_size_for_type[] = {0,
kCommandHeaderSize,
kAclHeaderSize,
kScoHeaderSize,
kEventHeaderSize,
kIsoHeaderSize};
const size_t packet_length_offset_for_type[] = {0,
kCommandLengthOffset,
kAclLengthOffset,
kScoLengthOffset,
kEventLengthOffset,
kIsoLengthOffset};
size_t HciGetPacketLengthForType(PacketType type,
const std::vector<uint8_t>& header) {
size_t offset = packet_length_offset_for_type[static_cast<uint8_t>(type)];
if (type != PacketType::ACL_DATA && type != PacketType::ISO_DATA) {
return header[offset];
}
return (((header[offset + 1]) << 8) | header[offset]);
}
} // namespace
const std::vector<uint8_t>& HciPacketizer::GetPacket() const { return packet_; }
bool HciPacketizer::OnDataReady(PacketType packet_type,
const std::vector<uint8_t>& buffer,
size_t offset) {
bool packet_completed = false;
size_t bytes_available = buffer.size() - offset;
switch (state_) {
case HCI_HEADER: {
size_t header_size =
header_size_for_type[static_cast<size_t>(packet_type)];
if (bytes_remaining_ == 0) {
bytes_remaining_ = header_size;
packet_.clear();
}
size_t bytes_to_copy = std::min(bytes_remaining_, bytes_available);
packet_.insert(packet_.end(), buffer.begin() + offset,
buffer.begin() + offset + bytes_to_copy);
bytes_remaining_ -= bytes_to_copy;
bytes_available -= bytes_to_copy;
if (bytes_remaining_ == 0) {
bytes_remaining_ = HciGetPacketLengthForType(packet_type, packet_);
if (bytes_remaining_ > 0) {
state_ = HCI_PAYLOAD;
if (bytes_available > 0) {
packet_completed =
OnDataReady(packet_type, buffer, offset + bytes_to_copy);
}
} else {
packet_completed = true;
}
}
break;
}
case HCI_PAYLOAD: {
size_t bytes_to_copy = std::min(bytes_remaining_, bytes_available);
packet_.insert(packet_.end(), buffer.begin() + offset,
buffer.begin() + offset + bytes_to_copy);
bytes_remaining_ -= bytes_to_copy;
if (bytes_remaining_ == 0) {
state_ = HCI_HEADER;
packet_completed = true;
}
break;
}
}
return packet_completed;
}
} // namespace android::hardware::bluetooth::hci

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2022 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 <functional>
#include <memory>
#include <vector>
#include "hci_internals.h"
namespace android::hardware::bluetooth::hci {
class HciPacketizer {
public:
HciPacketizer() = default;
bool OnDataReady(PacketType packet_type, const std::vector<uint8_t>& data,
size_t offset);
const std::vector<uint8_t>& GetPacket() const;
protected:
enum State { HCI_HEADER, HCI_PAYLOAD };
State state_{HCI_HEADER};
std::vector<uint8_t> packet_;
size_t bytes_remaining_{0};
};
} // namespace android::hardware::bluetooth::hci

View File

@@ -0,0 +1,441 @@
/*
* Copyright 2022 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 <log/log.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdint>
#include <cstring>
#include <future>
#include <vector>
#include "async_fd_watcher.h"
#include "log/log.h"
using android::hardware::bluetooth::async::AsyncFdWatcher;
using namespace android::hardware::bluetooth::hci;
using ::testing::Eq;
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 sample_data4[100] =
"A plane surface is a surface which lies evenly with the straight ...";
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.";
static char iso_data[100] =
"A plane angle is the inclination to one another of two lines in a ...";
MATCHER_P3(PacketMatches, header_, header_length, payload,
"Match header_length bytes of header and then the payload") {
size_t payload_length = strlen(payload);
if (header_length + payload_length != arg.size()) {
return false;
}
if (memcmp(header_, arg.data(), header_length) != 0) {
return false;
}
return memcmp(payload, arg.data() + header_length, payload_length) == 0;
};
ACTION_P(Notify, barrier) {
ALOGD("%s", __func__);
barrier->set_value();
}
class H4ProtocolTest : public ::testing::Test {
protected:
void SetUp() override {
ALOGD("%s", __func__);
int sockfd[2];
socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
chip_uart_fd_ = sockfd[1];
stack_uart_fd_ = sockfd[0];
h4_hci_ = std::make_shared<H4Protocol>(
stack_uart_fd_, cmd_cb_.AsStdFunction(), acl_cb_.AsStdFunction(),
sco_cb_.AsStdFunction(), event_cb_.AsStdFunction(),
iso_cb_.AsStdFunction(), disconnect_cb_.AsStdFunction());
}
void TearDown() override {
close(stack_uart_fd_);
close(chip_uart_fd_);
}
virtual void CallDataReady() { h4_hci_->OnDataReady(); }
void SendAndReadUartOutbound(PacketType type, char* data) {
ALOGD("%s sending", __func__);
int data_length = strlen(data);
h4_hci_->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(chip_uart_fd_, &read_fds);
TEMP_FAILURE_RETRY(
select(chip_uart_fd_ + 1, &read_fds, nullptr, nullptr, nullptr));
char byte;
TEMP_FAILURE_RETRY(read(chip_uart_fd_, &byte, 1));
EXPECT_EQ(i == 0 ? static_cast<uint8_t>(type) : data[i - 1], byte);
}
EXPECT_EQ(i, uart_length);
}
void ExpectInboundAclData(char* payload, std::promise<void>* promise) {
// h4 type[1] + handle[2] + size[2]
header_[0] = static_cast<uint8_t>(PacketType::ACL_DATA);
header_[1] = 19;
header_[2] = 92;
int length = strlen(payload);
header_[3] = length & 0xFF;
header_[4] = (length >> 8) & 0xFF;
ALOGD("(%d bytes) %s", length, payload);
EXPECT_CALL(acl_cb_,
Call(PacketMatches(header_ + 1, kAclHeaderSize, payload)))
.WillOnce(Notify(promise));
}
void WaitForTimeout(size_t timeout_ms, std::promise<void>* promise) {
auto future = promise->get_future();
auto status = future.wait_for(std::chrono::milliseconds(timeout_ms));
EXPECT_EQ(status, std::future_status::ready);
}
void WriteInboundAclData(char* payload) {
// Use the header_ computed in ExpectInboundAclData
TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kAclHeaderSize + 1));
TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
}
void ExpectInboundScoData(char* payload, std::promise<void>* promise) {
// h4 type[1] + handle[2] + size[1]
header_[0] = static_cast<uint8_t>(PacketType::SCO_DATA);
header_[1] = 20;
header_[2] = 17;
header_[3] = strlen(payload) & 0xFF;
EXPECT_CALL(sco_cb_,
Call(PacketMatches(header_ + 1, kScoHeaderSize, payload)))
.WillOnce(Notify(promise));
}
void WriteInboundScoData(char* payload) {
// Use the header_ computed in ExpectInboundScoData
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kScoHeaderSize + 1));
TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
}
void ExpectInboundEvent(char* payload, std::promise<void>* promise) {
// h4 type[1] + event_code[1] + size[1]
header_[0] = static_cast<uint8_t>(PacketType::EVENT);
header_[1] = 9;
header_[2] = strlen(payload) & 0xFF;
EXPECT_CALL(event_cb_,
Call(PacketMatches(header_ + 1, kEventHeaderSize, payload)))
.WillOnce(Notify(promise));
}
void WriteInboundEvent(char* payload) {
// Use the header_ computed in ExpectInboundEvent
char preamble[3] = {static_cast<uint8_t>(PacketType::EVENT), 9, 0};
preamble[2] = strlen(payload) & 0xFF;
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kEventHeaderSize + 1));
TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
}
void ExpectInboundIsoData(char* payload, std::promise<void>* promise) {
// h4 type[1] + handle[2] + size[1]
header_[0] = static_cast<uint8_t>(PacketType::ISO_DATA);
header_[1] = 19;
header_[2] = 92;
int length = strlen(payload);
header_[3] = length & 0xFF;
header_[4] = (length >> 8) & 0x3F;
EXPECT_CALL(iso_cb_,
Call(PacketMatches(header_ + 1, kIsoHeaderSize, payload)))
.WillOnce(Notify(promise));
}
void WriteInboundIsoData(char* payload) {
// Use the header_ computed in ExpectInboundIsoData
ALOGD("%s writing", __func__);
TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kIsoHeaderSize + 1));
TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
}
void WriteAndExpectManyInboundAclDataPackets(char* payload) {
size_t kNumPackets = 20;
// h4 type[1] + handle[2] + size[2]
char preamble[5] = {static_cast<uint8_t>(PacketType::ACL_DATA), 19, 92, 0,
0};
int length = strlen(payload);
preamble[3] = length & 0xFF;
preamble[4] = (length >> 8) & 0xFF;
EXPECT_CALL(acl_cb_, Call(PacketMatches(preamble + 1, sizeof(preamble) - 1,
payload)))
.Times(kNumPackets);
for (size_t i = 0; i < kNumPackets; i++) {
TEMP_FAILURE_RETRY(write(chip_uart_fd_, preamble, sizeof(preamble)));
TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
}
CallDataReady();
}
testing::MockFunction<void(const std::vector<uint8_t>&)> cmd_cb_;
testing::MockFunction<void(const std::vector<uint8_t>&)> event_cb_;
testing::MockFunction<void(const std::vector<uint8_t>&)> acl_cb_;
testing::MockFunction<void(const std::vector<uint8_t>&)> sco_cb_;
testing::MockFunction<void(const std::vector<uint8_t>&)> iso_cb_;
testing::MockFunction<void(void)> disconnect_cb_;
std::shared_ptr<H4Protocol> h4_hci_;
int chip_uart_fd_;
int stack_uart_fd_;
char header_[5];
};
// Test sending data sends correct data onto the UART
TEST_F(H4ProtocolTest, TestSends) {
SendAndReadUartOutbound(PacketType::COMMAND, sample_data1);
SendAndReadUartOutbound(PacketType::ACL_DATA, sample_data2);
SendAndReadUartOutbound(PacketType::SCO_DATA, sample_data3);
SendAndReadUartOutbound(PacketType::ISO_DATA, sample_data4);
}
// Ensure we properly parse data coming from the UART
TEST_F(H4ProtocolTest, TestReads) {
std::promise<void> acl_promise;
std::promise<void> sco_promise;
std::promise<void> event_promise;
std::promise<void> iso_promise;
ExpectInboundAclData(acl_data, &acl_promise);
WriteInboundAclData(acl_data);
CallDataReady();
ExpectInboundScoData(sco_data, &sco_promise);
WriteInboundScoData(sco_data);
CallDataReady();
ExpectInboundEvent(event_data, &event_promise);
WriteInboundEvent(event_data);
CallDataReady();
ExpectInboundIsoData(iso_data, &iso_promise);
WriteInboundIsoData(iso_data);
CallDataReady();
WaitForTimeout(100, &acl_promise);
WaitForTimeout(100, &sco_promise);
WaitForTimeout(100, &event_promise);
WaitForTimeout(100, &iso_promise);
}
TEST_F(H4ProtocolTest, TestMultiplePackets) {
WriteAndExpectManyInboundAclDataPackets(sco_data);
}
TEST_F(H4ProtocolTest, TestDisconnect) {
EXPECT_CALL(disconnect_cb_, Call());
close(chip_uart_fd_);
CallDataReady();
}
TEST_F(H4ProtocolTest, TestPartialWrites) {
size_t payload_len = strlen(acl_data);
const size_t kNumIntervals = payload_len + 1;
// h4 type[1] + handle[2] + size[2]
header_[0] = static_cast<uint8_t>(PacketType::ACL_DATA);
header_[1] = 19;
header_[2] = 92;
header_[3] = payload_len & 0xFF;
header_[4] = (payload_len >> 8) & 0xFF;
EXPECT_CALL(acl_cb_,
Call(PacketMatches(header_ + 1, sizeof(header_) - 1, acl_data)))
.Times(kNumIntervals);
for (size_t interval = 1; interval < kNumIntervals + 1; interval++) {
// Use the header_ data that expect already set up.
if (interval < kAclHeaderSize) {
TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, interval));
CallDataReady();
TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_ + interval,
kAclHeaderSize + 1 - interval));
CallDataReady();
} else {
TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, kAclHeaderSize + 1));
CallDataReady();
}
for (size_t bytes = 0; bytes + interval <= payload_len; bytes += interval) {
TEMP_FAILURE_RETRY(write(chip_uart_fd_, acl_data + bytes, interval));
CallDataReady();
}
size_t extra_bytes = payload_len % interval;
if (extra_bytes) {
TEMP_FAILURE_RETRY(write(
chip_uart_fd_, acl_data + payload_len - extra_bytes, extra_bytes));
CallDataReady();
}
}
}
class H4ProtocolAsyncTest : public H4ProtocolTest {
protected:
void SetUp() override {
H4ProtocolTest::SetUp();
fd_watcher_.WatchFdForNonBlockingReads(
stack_uart_fd_, [this](int) { h4_hci_->OnDataReady(); });
}
void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); }
void CallDataReady() override {
// The Async test can't call data ready.
FAIL();
}
void SendAndReadUartOutbound(PacketType type, char* data) {
ALOGD("%s sending", __func__);
int data_length = strlen(data);
h4_hci_->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(chip_uart_fd_, &read_fds);
TEMP_FAILURE_RETRY(
select(chip_uart_fd_ + 1, &read_fds, nullptr, nullptr, nullptr));
char byte;
TEMP_FAILURE_RETRY(read(chip_uart_fd_, &byte, 1));
EXPECT_EQ(i == 0 ? static_cast<uint8_t>(type) : data[i - 1], byte);
}
EXPECT_EQ(i, uart_length);
}
void WriteAndExpectInboundAclData(char* payload) {
std::promise<void> promise;
ExpectInboundAclData(payload, &promise);
WriteInboundAclData(payload);
WaitForTimeout(100, &promise);
}
void WriteAndExpectInboundScoData(char* payload) {
std::promise<void> promise;
ExpectInboundScoData(payload, &promise);
WriteInboundScoData(payload);
WaitForTimeout(100, &promise);
}
void WriteAndExpectInboundEvent(char* payload) {
std::promise<void> promise;
ExpectInboundEvent(payload, &promise);
WriteInboundEvent(payload);
WaitForTimeout(100, &promise);
}
void WriteAndExpectInboundIsoData(char* payload) {
std::promise<void> promise;
ExpectInboundIsoData(payload, &promise);
WriteInboundIsoData(payload);
WaitForTimeout(100, &promise);
}
void WriteAndExpectManyInboundAclDataPackets(char* payload) {
const size_t kNumPackets = 20;
// h4 type[1] + handle[2] + size[2]
char preamble[5] = {static_cast<uint8_t>(PacketType::ACL_DATA), 19, 92, 0,
0};
int length = strlen(payload);
preamble[3] = length & 0xFF;
preamble[4] = (length >> 8) & 0xFF;
EXPECT_CALL(acl_cb_, Call(PacketMatches(preamble + 1, sizeof(preamble) - 1,
payload)))
.Times(kNumPackets);
for (size_t i = 0; i < kNumPackets; i++) {
TEMP_FAILURE_RETRY(write(chip_uart_fd_, preamble, sizeof(preamble)));
TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload)));
}
WriteAndExpectInboundEvent(event_data);
}
AsyncFdWatcher fd_watcher_;
};
// Test sending data sends correct data onto the UART
TEST_F(H4ProtocolAsyncTest, TestSends) {
SendAndReadUartOutbound(PacketType::COMMAND, sample_data1);
SendAndReadUartOutbound(PacketType::ACL_DATA, sample_data2);
SendAndReadUartOutbound(PacketType::SCO_DATA, sample_data3);
SendAndReadUartOutbound(PacketType::ISO_DATA, sample_data4);
}
// Ensure we properly parse data coming from the UART
TEST_F(H4ProtocolAsyncTest, TestReads) {
WriteAndExpectInboundAclData(acl_data);
WriteAndExpectInboundScoData(sco_data);
WriteAndExpectInboundEvent(event_data);
WriteAndExpectInboundIsoData(iso_data);
}
TEST_F(H4ProtocolAsyncTest, TestMultiplePackets) {
WriteAndExpectManyInboundAclDataPackets(sco_data);
}
TEST_F(H4ProtocolAsyncTest, TestDisconnect) {
std::promise<void> promise;
EXPECT_CALL(disconnect_cb_, Call()).WillOnce(Notify(&promise));
close(chip_uart_fd_);
// Fail if it takes longer than 100 ms.
WaitForTimeout(100, &promise);
}

View File

@@ -151,6 +151,13 @@
<instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.bluetooth</name>
<interface>
<name>IBluetoothHci</name>
<instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.bluetooth.audio</name>
<version>2</version>