bluetooth: add aidl hal
* Copy from hardware/interfaces/bluetooth/aidl/default at 488942f82bd1bc9ad1cb65a02c71421dc3a6a3d6.
This commit is contained in:
85
bluetooth/Android.bp
Normal file
85
bluetooth/Android.bp
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
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",
|
||||||
|
"net_bluetooth_mgmt.cpp",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_binary {
|
||||||
|
name: "android.hardware.bluetooth-service.default",
|
||||||
|
relative_install_path: "hw",
|
||||||
|
init_rc: ["bluetooth-service-default.rc"],
|
||||||
|
vintf_fragments: [":manifest_android.hardware.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",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
filegroup {
|
||||||
|
name: "manifest_android.hardware.bluetooth-service.default.xml",
|
||||||
|
srcs: ["bluetooth-service-default.xml"],
|
||||||
|
}
|
364
bluetooth/BluetoothHci.cpp
Normal file
364
bluetooth/BluetoothHci.cpp
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
using aidl::android::hardware::bluetooth::Status;
|
||||||
|
|
||||||
|
namespace aidl::android::hardware::bluetooth::impl {
|
||||||
|
|
||||||
|
void OnDeath(void* cookie);
|
||||||
|
|
||||||
|
std::optional<std::string> GetSystemProperty(const std::string& property) {
|
||||||
|
std::array<char, PROPERTY_VALUE_MAX> value_array{0};
|
||||||
|
auto value_len = property_get(property.c_str(), value_array.data(), nullptr);
|
||||||
|
if (value_len <= 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return std::string(value_array.data(), value_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool starts_with(const std::string& str, const std::string& prefix) {
|
||||||
|
return str.compare(0, prefix.length(), prefix) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mHasDiedMutex);
|
||||||
|
has_died_ = true;
|
||||||
|
}
|
||||||
|
mHci->close();
|
||||||
|
}
|
||||||
|
BluetoothHci* mHci;
|
||||||
|
std::shared_ptr<IBluetoothHciCallbacks> mCb;
|
||||||
|
AIBinder_DeathRecipient* clientDeathRecipient_;
|
||||||
|
bool getHasDied() {
|
||||||
|
std::lock_guard<std::mutex> guard(mHasDiedMutex);
|
||||||
|
return has_died_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mHasDiedMutex;
|
||||||
|
bool has_died_{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BluetoothHci::getFdFromDevPath() {
|
||||||
|
int fd = open(mDevPath.c_str(), O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
ALOGE("Could not connect to bt: %s (%s)", mDevPath.c_str(),
|
||||||
|
strerror(errno));
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
if (int ret = SetTerminalRaw(fd) < 0) {
|
||||||
|
ALOGI("Could not make %s a raw terminal %d(%s)", mDevPath.c_str(), ret,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BluetoothHci::reset() {
|
||||||
|
// Send a reset command and wait until the command complete comes back.
|
||||||
|
|
||||||
|
std::vector<uint8_t> reset = {0x03, 0x0c, 0x00};
|
||||||
|
|
||||||
|
auto resetPromise = std::make_shared<std::promise<void>>();
|
||||||
|
auto resetFuture = resetPromise->get_future();
|
||||||
|
|
||||||
|
mH4 = std::make_shared<H4Protocol>(
|
||||||
|
mFd,
|
||||||
|
[](const std::vector<uint8_t>& raw_command) {
|
||||||
|
ALOGI("Discarding %d bytes with command type",
|
||||||
|
static_cast<int>(raw_command.size()));
|
||||||
|
},
|
||||||
|
[](const std::vector<uint8_t>& raw_acl) {
|
||||||
|
ALOGI("Discarding %d bytes with acl type",
|
||||||
|
static_cast<int>(raw_acl.size()));
|
||||||
|
},
|
||||||
|
[](const std::vector<uint8_t>& raw_sco) {
|
||||||
|
ALOGI("Discarding %d bytes with sco type",
|
||||||
|
static_cast<int>(raw_sco.size()));
|
||||||
|
},
|
||||||
|
[resetPromise](const std::vector<uint8_t>& raw_event) {
|
||||||
|
std::vector<uint8_t> reset_complete = {0x0e, 0x04, 0x01,
|
||||||
|
0x03, 0x0c, 0x00};
|
||||||
|
bool valid = raw_event.size() == 6 &&
|
||||||
|
raw_event[0] == reset_complete[0] &&
|
||||||
|
raw_event[1] == reset_complete[1] &&
|
||||||
|
// Don't compare the number of packets field.
|
||||||
|
raw_event[3] == reset_complete[3] &&
|
||||||
|
raw_event[4] == reset_complete[4] &&
|
||||||
|
raw_event[5] == reset_complete[5];
|
||||||
|
if (valid) {
|
||||||
|
resetPromise->set_value();
|
||||||
|
} else {
|
||||||
|
ALOGI("Discarding %d bytes with event type",
|
||||||
|
static_cast<int>(raw_event.size()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[](const std::vector<uint8_t>& raw_iso) {
|
||||||
|
ALOGI("Discarding %d bytes with iso type",
|
||||||
|
static_cast<int>(raw_iso.size()));
|
||||||
|
},
|
||||||
|
[this]() {
|
||||||
|
ALOGI("HCI socket device disconnected while waiting for reset");
|
||||||
|
mFdWatcher.StopWatchingFileDescriptors();
|
||||||
|
});
|
||||||
|
mFdWatcher.WatchFdForNonBlockingReads(mFd,
|
||||||
|
[this](int) { mH4->OnDataReady(); });
|
||||||
|
|
||||||
|
ndk::ScopedAStatus result = send(PacketType::COMMAND, reset);
|
||||||
|
if (!result.isOk()) {
|
||||||
|
ALOGE("Error sending reset command");
|
||||||
|
}
|
||||||
|
auto status = resetFuture.wait_for(std::chrono::seconds(1));
|
||||||
|
mFdWatcher.StopWatchingFileDescriptors();
|
||||||
|
if (status == std::future_status::ready) {
|
||||||
|
ALOGI("HCI Reset successful");
|
||||||
|
} else {
|
||||||
|
ALOGE("HCI Reset Response not received in one second");
|
||||||
|
}
|
||||||
|
|
||||||
|
resetPromise.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus BluetoothHci::initialize(
|
||||||
|
const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
|
||||||
|
ALOGI(__func__);
|
||||||
|
|
||||||
|
if (cb == nullptr) {
|
||||||
|
ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
|
||||||
|
return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
HalState old_state = HalState::READY;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mStateMutex);
|
||||||
|
if (mState != HalState::READY) {
|
||||||
|
old_state = mState;
|
||||||
|
} else {
|
||||||
|
mState = HalState::INITIALIZING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_state != HalState::READY) {
|
||||||
|
ALOGE("initialize: Unexpected State %d", static_cast<int>(old_state));
|
||||||
|
close();
|
||||||
|
cb->initializationComplete(Status::ALREADY_INITIALIZED);
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
mCb = cb;
|
||||||
|
management_.reset(new NetBluetoothMgmt);
|
||||||
|
mFd = management_->openHci();
|
||||||
|
if (mFd < 0) {
|
||||||
|
management_.reset();
|
||||||
|
|
||||||
|
ALOGI("Unable to open Linux interface, trying default path.");
|
||||||
|
mFd = getFdFromDevPath();
|
||||||
|
if (mFd < 0) {
|
||||||
|
mState = HalState::READY;
|
||||||
|
cb->initializationComplete(Status::UNABLE_TO_OPEN_INTERFACE);
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mDeathRecipient->LinkToDeath(mCb);
|
||||||
|
|
||||||
|
// TODO: HCI Reset on emulators since the bluetooth controller
|
||||||
|
// cannot be powered on/off during the HAL setup; and the stack
|
||||||
|
// might received spurious packets/events during boottime.
|
||||||
|
// Proper solution would be to use bt-virtio or vsock to better
|
||||||
|
// control the link to rootcanal and the controller lifetime.
|
||||||
|
const std::string kBoardProperty = "ro.product.board";
|
||||||
|
const std::string kCuttlefishBoard = "cutf";
|
||||||
|
auto board_name = GetSystemProperty(kBoardProperty);
|
||||||
|
if (board_name.has_value() && (
|
||||||
|
starts_with(board_name.value(), "cutf") ||
|
||||||
|
starts_with(board_name.value(), "goldfish"))) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
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_acl) {
|
||||||
|
mCb->aclDataReceived(raw_acl);
|
||||||
|
},
|
||||||
|
[this](const std::vector<uint8_t>& raw_sco) {
|
||||||
|
mCb->scoDataReceived(raw_sco);
|
||||||
|
},
|
||||||
|
[this](const std::vector<uint8_t>& raw_event) {
|
||||||
|
mCb->hciEventReceived(raw_event);
|
||||||
|
},
|
||||||
|
[this](const std::vector<uint8_t>& raw_iso) {
|
||||||
|
mCb->isoDataReceived(raw_iso);
|
||||||
|
},
|
||||||
|
[this]() {
|
||||||
|
ALOGI("HCI socket device disconnected");
|
||||||
|
mFdWatcher.StopWatchingFileDescriptors();
|
||||||
|
});
|
||||||
|
mFdWatcher.WatchFdForNonBlockingReads(mFd,
|
||||||
|
[this](int) { mH4->OnDataReady(); });
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mStateMutex);
|
||||||
|
mState = HalState::ONE_CLIENT;
|
||||||
|
}
|
||||||
|
ALOGI("initialization complete");
|
||||||
|
auto status = mCb->initializationComplete(Status::SUCCESS);
|
||||||
|
if (!status.isOk()) {
|
||||||
|
if (!mDeathRecipient->getHasDied()) {
|
||||||
|
ALOGE("Error sending init callback, but no death notification");
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
return ndk::ScopedAStatus::fromServiceSpecificError(
|
||||||
|
STATUS_FAILED_TRANSACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus BluetoothHci::close() {
|
||||||
|
ALOGI(__func__);
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mStateMutex);
|
||||||
|
if (mState != HalState::ONE_CLIENT) {
|
||||||
|
LOG_ALWAYS_FATAL_IF(mState == HalState::INITIALIZING,
|
||||||
|
"mState is INITIALIZING");
|
||||||
|
ALOGI("Already closed");
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
mState = HalState::CLOSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
mFdWatcher.StopWatchingFileDescriptors();
|
||||||
|
|
||||||
|
if (management_) {
|
||||||
|
management_->closeHci();
|
||||||
|
} else {
|
||||||
|
::close(mFd);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mStateMutex);
|
||||||
|
mState = HalState::READY;
|
||||||
|
mH4 = nullptr;
|
||||||
|
}
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus BluetoothHci::sendHciCommand(
|
||||||
|
const std::vector<uint8_t>& packet) {
|
||||||
|
return send(PacketType::COMMAND, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus BluetoothHci::sendAclData(
|
||||||
|
const std::vector<uint8_t>& packet) {
|
||||||
|
return send(PacketType::ACL_DATA, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus BluetoothHci::sendScoData(
|
||||||
|
const std::vector<uint8_t>& packet) {
|
||||||
|
return send(PacketType::SCO_DATA, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus BluetoothHci::sendIsoData(
|
||||||
|
const std::vector<uint8_t>& packet) {
|
||||||
|
return send(PacketType::ISO_DATA, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
ndk::ScopedAStatus BluetoothHci::send(PacketType type,
|
||||||
|
const std::vector<uint8_t>& v) {
|
||||||
|
if (v.empty()) {
|
||||||
|
ALOGE("Packet is empty, no data was found to be sent");
|
||||||
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(mStateMutex);
|
||||||
|
if (mH4 == nullptr) {
|
||||||
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
mH4->Send(type, v);
|
||||||
|
return ndk::ScopedAStatus::ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::bluetooth::impl
|
87
bluetooth/BluetoothHci.h
Normal file
87
bluetooth/BluetoothHci.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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 <future>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "async_fd_watcher.h"
|
||||||
|
#include "h4_protocol.h"
|
||||||
|
#include "net_bluetooth_mgmt.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;
|
||||||
|
|
||||||
|
int getFdFromDevPath();
|
||||||
|
[[nodiscard]] ndk::ScopedAStatus send(
|
||||||
|
::android::hardware::bluetooth::hci::PacketType type,
|
||||||
|
const std::vector<uint8_t>& packet);
|
||||||
|
std::unique_ptr<NetBluetoothMgmt> management_{};
|
||||||
|
|
||||||
|
// Send a reset command and discard all packets until a reset is received.
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// Don't close twice or open before close is complete
|
||||||
|
std::mutex mStateMutex;
|
||||||
|
enum class HalState {
|
||||||
|
READY,
|
||||||
|
INITIALIZING,
|
||||||
|
ONE_CLIENT,
|
||||||
|
CLOSING,
|
||||||
|
} mState{HalState::READY};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::bluetooth::impl
|
6
bluetooth/bluetooth-service-default.rc
Normal file
6
bluetooth/bluetooth-service-default.rc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
service vendor.bluetooth-default /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/bluetooth-service-default.xml
Normal file
6
bluetooth/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>
|
307
bluetooth/net_bluetooth_mgmt.cpp
Normal file
307
bluetooth/net_bluetooth_mgmt.cpp
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* 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 "net_bluetooth_mgmt.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <log/log.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
// Definitions imported from <linux/net/bluetooth/bluetooth.h>
|
||||||
|
#define BTPROTO_HCI 1
|
||||||
|
|
||||||
|
// Definitions imported from <linux/net/bluetooth/hci_sock.h>
|
||||||
|
#define HCI_CHANNEL_USER 1
|
||||||
|
#define HCI_CHANNEL_CONTROL 3
|
||||||
|
#define HCI_DEV_NONE 0xffff
|
||||||
|
|
||||||
|
struct sockaddr_hci {
|
||||||
|
sa_family_t hci_family;
|
||||||
|
unsigned short hci_dev;
|
||||||
|
unsigned short hci_channel;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Definitions imported from <linux/net/bluetooth/mgmt.h>
|
||||||
|
#define MGMT_OP_READ_INDEX_LIST 0x0003
|
||||||
|
#define MGMT_EV_INDEX_ADDED 0x0004
|
||||||
|
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||||
|
#define MGMT_PKT_SIZE_MAX 1024
|
||||||
|
#define MGMT_INDEX_NONE 0xFFFF
|
||||||
|
|
||||||
|
struct mgmt_pkt {
|
||||||
|
uint16_t opcode;
|
||||||
|
uint16_t index;
|
||||||
|
uint16_t len;
|
||||||
|
uint8_t data[MGMT_PKT_SIZE_MAX];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct mgmt_ev_read_index_list {
|
||||||
|
uint16_t opcode;
|
||||||
|
uint8_t status;
|
||||||
|
uint16_t num_controllers;
|
||||||
|
uint16_t index[];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
// Definitions imported from <linux/rfkill.h>
|
||||||
|
#define RFKILL_STATE_SOFT_BLOCKED 0
|
||||||
|
#define RFKILL_STATE_UNBLOCKED 1
|
||||||
|
#define RFKILL_STATE_HARD_BLOCKED 2
|
||||||
|
|
||||||
|
#define RFKILL_TYPE_BLUETOOTH 2
|
||||||
|
|
||||||
|
#define RFKILL_OP_ADD 0
|
||||||
|
#define RFKILL_OP_CHANGE 2
|
||||||
|
|
||||||
|
struct rfkill_event {
|
||||||
|
uint32_t idx;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t op;
|
||||||
|
uint8_t soft;
|
||||||
|
uint8_t hard;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
namespace aidl::android::hardware::bluetooth::impl {
|
||||||
|
|
||||||
|
// Wait indefinitely for the selected HCI interface to be enabled in the
|
||||||
|
// bluetooth driver.
|
||||||
|
int NetBluetoothMgmt::waitHciDev(int hci_interface) {
|
||||||
|
ALOGI("waiting for hci interface %d", hci_interface);
|
||||||
|
|
||||||
|
int ret = -1;
|
||||||
|
struct mgmt_pkt cmd;
|
||||||
|
struct pollfd pollfd;
|
||||||
|
struct sockaddr_hci hci_addr = {
|
||||||
|
.hci_family = AF_BLUETOOTH,
|
||||||
|
.hci_dev = HCI_DEV_NONE,
|
||||||
|
.hci_channel = HCI_CHANNEL_CONTROL,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Open and bind a socket to the bluetooth control interface in the
|
||||||
|
// kernel driver, used to send control commands and receive control
|
||||||
|
// events.
|
||||||
|
int fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
|
||||||
|
if (fd < 0) {
|
||||||
|
ALOGE("unable to open raw bluetooth socket: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(fd, (struct sockaddr*)&hci_addr, sizeof(hci_addr)) < 0) {
|
||||||
|
ALOGE("unable to bind bluetooth control channel: %s", strerror(errno));
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the control command [Read Index List].
|
||||||
|
cmd = {
|
||||||
|
.opcode = MGMT_OP_READ_INDEX_LIST,
|
||||||
|
.index = MGMT_INDEX_NONE,
|
||||||
|
.len = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (write(fd, &cmd, 6) != 6) {
|
||||||
|
ALOGE("error writing mgmt command: %s", strerror(errno));
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll the control socket waiting for the command response,
|
||||||
|
// and subsequent [Index Added] events. The loops continue without
|
||||||
|
// timeout until the selected hci interface is detected.
|
||||||
|
pollfd = {.fd = fd, .events = POLLIN};
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ret = poll(&pollfd, 1, -1);
|
||||||
|
|
||||||
|
// Poll interrupted, try again.
|
||||||
|
if (ret == -1 && (errno == EINTR || errno == EAGAIN)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll failure, abandon.
|
||||||
|
if (ret == -1) {
|
||||||
|
ALOGE("poll error: %s", strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spurious wakeup, try again.
|
||||||
|
if (ret == 0 || (pollfd.revents & POLLIN) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the next control event.
|
||||||
|
struct mgmt_pkt ev {};
|
||||||
|
ret = read(fd, &ev, sizeof(ev));
|
||||||
|
if (ret < 0) {
|
||||||
|
ALOGE("error reading mgmt event: %s", strerror(errno));
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Received [Read Index List] command response.
|
||||||
|
if (ev.opcode == MGMT_EV_CMD_COMPLETE) {
|
||||||
|
struct mgmt_ev_read_index_list* data =
|
||||||
|
(struct mgmt_ev_read_index_list*)ev.data;
|
||||||
|
|
||||||
|
// Prefer the exact hci_interface
|
||||||
|
for (int i = 0; i < data->num_controllers; i++) {
|
||||||
|
if (data->index[i] == hci_interface) {
|
||||||
|
ALOGI("hci interface %d found", data->index[i]);
|
||||||
|
ret = data->index[i];
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept a larger one if we can't find the exact one
|
||||||
|
for (int i = 0; i < data->num_controllers; i++) {
|
||||||
|
if (data->index[i] >= hci_interface) {
|
||||||
|
ALOGI("hci interface %d found", data->index[i]);
|
||||||
|
ret = data->index[i];
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Received [Index Added] event.
|
||||||
|
if (ev.opcode == MGMT_EV_INDEX_ADDED && ev.index == hci_interface) {
|
||||||
|
ALOGI("hci interface %d added", hci_interface);
|
||||||
|
ret = hci_interface;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
::close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NetBluetoothMgmt::openRfkill() {
|
||||||
|
int fd = open("/dev/rfkill", O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
ALOGE("unable to open /dev/rfkill: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
|
||||||
|
ALOGE("unable to set rfkill control device to non-blocking: %s",
|
||||||
|
strerror(errno));
|
||||||
|
::close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct rfkill_event event {};
|
||||||
|
ssize_t res = read(fd, &event, sizeof(event));
|
||||||
|
if (res < 0) {
|
||||||
|
ALOGE("error reading rfkill events: %s", strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALOGI("index:%d type:%d op:%d", event.idx, event.type, event.op);
|
||||||
|
|
||||||
|
if (event.op == RFKILL_OP_ADD && event.type == RFKILL_TYPE_BLUETOOTH) {
|
||||||
|
rfkill_bt_index_ = event.idx;
|
||||||
|
rfkill_fd_ = fd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block or unblock Bluetooth.
|
||||||
|
int NetBluetoothMgmt::rfkill(int block) {
|
||||||
|
if (rfkill_fd_ == -1) {
|
||||||
|
openRfkill();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rfkill_fd_ == -1) {
|
||||||
|
ALOGE("rfkill unavailable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rfkill_event event = {
|
||||||
|
.idx = static_cast<uint32_t>(rfkill_bt_index_),
|
||||||
|
.type = RFKILL_TYPE_BLUETOOTH,
|
||||||
|
.op = RFKILL_OP_CHANGE,
|
||||||
|
.soft = static_cast<uint8_t>(block),
|
||||||
|
.hard = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
int res = write(rfkill_fd_, &event, sizeof(event));
|
||||||
|
if (res < 0) {
|
||||||
|
ALOGE("error writing rfkill command: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NetBluetoothMgmt::openHci(int hci_interface) {
|
||||||
|
ALOGI("opening hci interface %d", hci_interface);
|
||||||
|
|
||||||
|
// Block Bluetooth.
|
||||||
|
rfkill(1);
|
||||||
|
|
||||||
|
// Wait for the HCI interface to complete initialization or to come online.
|
||||||
|
int hci = waitHciDev(hci_interface);
|
||||||
|
if (hci < 0) {
|
||||||
|
ALOGE("hci interface %d not found", hci_interface);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the raw HCI socket.
|
||||||
|
int fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
|
||||||
|
if (fd < 0) {
|
||||||
|
ALOGE("unable to open raw bluetooth socket: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_hci hci_addr = {
|
||||||
|
.hci_family = AF_BLUETOOTH,
|
||||||
|
.hci_dev = static_cast<uint16_t>(hci),
|
||||||
|
.hci_channel = HCI_CHANNEL_USER,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind the socket to the selected interface.
|
||||||
|
if (bind(fd, (struct sockaddr*)&hci_addr, sizeof(hci_addr)) < 0) {
|
||||||
|
ALOGE("unable to bind bluetooth user channel: %s", strerror(errno));
|
||||||
|
::close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALOGI("hci interface %d ready", hci);
|
||||||
|
bt_fd_ = fd;
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetBluetoothMgmt::closeHci() {
|
||||||
|
if (bt_fd_ != -1) {
|
||||||
|
::close(bt_fd_);
|
||||||
|
bt_fd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unblock Bluetooth.
|
||||||
|
rfkill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::bluetooth::impl
|
47
bluetooth/net_bluetooth_mgmt.h
Normal file
47
bluetooth/net_bluetooth_mgmt.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
|
||||||
|
namespace aidl::android::hardware::bluetooth::impl {
|
||||||
|
|
||||||
|
class NetBluetoothMgmt {
|
||||||
|
public:
|
||||||
|
NetBluetoothMgmt() {}
|
||||||
|
~NetBluetoothMgmt() {
|
||||||
|
::close(rfkill_fd_);
|
||||||
|
::close(bt_fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int openHci(int hci_interface = 0);
|
||||||
|
void closeHci();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int waitHciDev(int hci_interface);
|
||||||
|
int openRfkill();
|
||||||
|
int rfkill(int block);
|
||||||
|
|
||||||
|
// Index of the first rfkill device of type bluetooth.
|
||||||
|
int rfkill_bt_index_{-1};
|
||||||
|
// File descriptor opened to /dev/rfkill.
|
||||||
|
int rfkill_fd_{-1};
|
||||||
|
// File descriptor opened to the bluetooth user channel.
|
||||||
|
int bt_fd_{-1};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aidl::android::hardware::bluetooth::impl
|
49
bluetooth/service.cpp
Normal file
49
bluetooth/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(0)) {
|
||||||
|
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;
|
||||||
|
}
|
Reference in New Issue
Block a user