mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
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:
28
bluetooth/aidl/Android.bp
Normal file
28
bluetooth/aidl/Android.bp
Normal 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",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
73
bluetooth/aidl/android/hardware/bluetooth/IBluetoothHci.aidl
Normal file
73
bluetooth/aidl/android/hardware/bluetooth/IBluetoothHci.aidl
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
27
bluetooth/aidl/android/hardware/bluetooth/Status.aidl
Normal file
27
bluetooth/aidl/android/hardware/bluetooth/Status.aidl
Normal 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,
|
||||
}
|
||||
79
bluetooth/aidl/default/Android.bp
Normal file
79
bluetooth/aidl/default/Android.bp
Normal 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",
|
||||
],
|
||||
},
|
||||
}
|
||||
194
bluetooth/aidl/default/BluetoothHci.cpp
Normal file
194
bluetooth/aidl/default/BluetoothHci.cpp
Normal 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
|
||||
71
bluetooth/aidl/default/BluetoothHci.h
Normal file
71
bluetooth/aidl/default/BluetoothHci.h
Normal 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
|
||||
6
bluetooth/aidl/default/bluetooth-service-default.rc
Normal file
6
bluetooth/aidl/default/bluetooth-service-default.rc
Normal 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
|
||||
6
bluetooth/aidl/default/bluetooth-service-default.xml
Normal file
6
bluetooth/aidl/default/bluetooth-service-default.xml
Normal 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>
|
||||
49
bluetooth/aidl/default/service.cpp
Normal file
49
bluetooth/aidl/default/service.cpp
Normal 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;
|
||||
}
|
||||
32
bluetooth/aidl/default/test/fuzzer.cpp
Normal file
32
bluetooth/aidl/default/test/fuzzer.cpp
Normal 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;
|
||||
}
|
||||
41
bluetooth/async/Android.bp
Normal file
41
bluetooth/async/Android.bp
Normal 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"],
|
||||
}
|
||||
176
bluetooth/async/async_fd_watcher.cc
Normal file
176
bluetooth/async/async_fd_watcher.cc
Normal 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
|
||||
60
bluetooth/async/async_fd_watcher.h
Normal file
60
bluetooth/async/async_fd_watcher.h
Normal 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
|
||||
377
bluetooth/async/test/async_fd_watcher_unittest.cc
Normal file
377
bluetooth/async/test/async_fd_watcher_unittest.cc
Normal 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
46
bluetooth/hci/Android.bp
Normal 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"],
|
||||
}
|
||||
144
bluetooth/hci/h4_protocol.cc
Normal file
144
bluetooth/hci/h4_protocol.cc
Normal 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
|
||||
68
bluetooth/hci/h4_protocol.h
Normal file
68
bluetooth/hci/h4_protocol.h
Normal 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
|
||||
55
bluetooth/hci/hci_internals.h
Normal file
55
bluetooth/hci/hci_internals.h
Normal 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
|
||||
100
bluetooth/hci/hci_packetizer.cc
Normal file
100
bluetooth/hci/hci_packetizer.cc
Normal 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
|
||||
41
bluetooth/hci/hci_packetizer.h
Normal file
41
bluetooth/hci/hci_packetizer.h
Normal 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
|
||||
441
bluetooth/hci/test/h4_protocol_unittest.cc
Normal file
441
bluetooth/hci/test/h4_protocol_unittest.cc
Normal 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);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user