diff --git a/bluetooth/Android.bp b/bluetooth/Android.bp new file mode 100644 index 0000000..3f4ba99 --- /dev/null +++ b/bluetooth/Android.bp @@ -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"], +} diff --git a/bluetooth/BluetoothHci.cpp b/bluetooth/BluetoothHci.cpp new file mode 100644 index 0000000..a247cb0 --- /dev/null +++ b/bluetooth/BluetoothHci.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 GetSystemProperty(const std::string& property) { + std::array 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& 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& 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 guard(mHasDiedMutex); + has_died_ = true; + } + mHci->close(); + } + BluetoothHci* mHci; + std::shared_ptr mCb; + AIBinder_DeathRecipient* clientDeathRecipient_; + bool getHasDied() { + std::lock_guard guard(mHasDiedMutex); + return has_died_; + } + + private: + std::mutex mHasDiedMutex; + bool has_died_{false}; +}; + +void OnDeath(void* cookie) { + auto* death_recipient = static_cast(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(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 reset = {0x03, 0x0c, 0x00}; + + auto resetPromise = std::make_shared>(); + auto resetFuture = resetPromise->get_future(); + + mH4 = std::make_shared( + mFd, + [](const std::vector& raw_command) { + ALOGI("Discarding %d bytes with command type", + static_cast(raw_command.size())); + }, + [](const std::vector& raw_acl) { + ALOGI("Discarding %d bytes with acl type", + static_cast(raw_acl.size())); + }, + [](const std::vector& raw_sco) { + ALOGI("Discarding %d bytes with sco type", + static_cast(raw_sco.size())); + }, + [resetPromise](const std::vector& raw_event) { + std::vector 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(raw_event.size())); + } + }, + [](const std::vector& raw_iso) { + ALOGI("Discarding %d bytes with iso type", + static_cast(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& 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 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(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( + mFd, + [](const std::vector& /* raw_command */) { + LOG_ALWAYS_FATAL("Unexpected command!"); + }, + [this](const std::vector& raw_acl) { + mCb->aclDataReceived(raw_acl); + }, + [this](const std::vector& raw_sco) { + mCb->scoDataReceived(raw_sco); + }, + [this](const std::vector& raw_event) { + mCb->hciEventReceived(raw_event); + }, + [this](const std::vector& raw_iso) { + mCb->isoDataReceived(raw_iso); + }, + [this]() { + ALOGI("HCI socket device disconnected"); + mFdWatcher.StopWatchingFileDescriptors(); + }); + mFdWatcher.WatchFdForNonBlockingReads(mFd, + [this](int) { mH4->OnDataReady(); }); + + { + std::lock_guard 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 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 guard(mStateMutex); + mState = HalState::READY; + mH4 = nullptr; + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus BluetoothHci::sendHciCommand( + const std::vector& packet) { + return send(PacketType::COMMAND, packet); +} + +ndk::ScopedAStatus BluetoothHci::sendAclData( + const std::vector& packet) { + return send(PacketType::ACL_DATA, packet); +} + +ndk::ScopedAStatus BluetoothHci::sendScoData( + const std::vector& packet) { + return send(PacketType::SCO_DATA, packet); +} + +ndk::ScopedAStatus BluetoothHci::sendIsoData( + const std::vector& packet) { + return send(PacketType::ISO_DATA, packet); +} + +ndk::ScopedAStatus BluetoothHci::send(PacketType type, + const std::vector& 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 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 diff --git a/bluetooth/BluetoothHci.h b/bluetooth/BluetoothHci.h new file mode 100644 index 0000000..477cc5c --- /dev/null +++ b/bluetooth/BluetoothHci.h @@ -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 +#include + +#include +#include + +#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& cb) override; + + ndk::ScopedAStatus sendHciCommand( + const std::vector& packet) override; + + ndk::ScopedAStatus sendAclData(const std::vector& packet) override; + + ndk::ScopedAStatus sendScoData(const std::vector& packet) override; + + ndk::ScopedAStatus sendIsoData(const std::vector& packet) override; + + ndk::ScopedAStatus close() override; + + static void OnPacketReady(); + + static BluetoothHci* get(); + + private: + int mFd{-1}; + std::shared_ptr mCb = nullptr; + + std::shared_ptr<::android::hardware::bluetooth::hci::H4Protocol> mH4; + + std::shared_ptr 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& packet); + std::unique_ptr 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 diff --git a/bluetooth/bluetooth-service-default.rc b/bluetooth/bluetooth-service-default.rc new file mode 100644 index 0000000..dc78698 --- /dev/null +++ b/bluetooth/bluetooth-service-default.rc @@ -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 diff --git a/bluetooth/bluetooth-service-default.xml b/bluetooth/bluetooth-service-default.xml new file mode 100644 index 0000000..bb05995 --- /dev/null +++ b/bluetooth/bluetooth-service-default.xml @@ -0,0 +1,6 @@ + + + android.hardware.bluetooth + IBluetoothHci/default + + diff --git a/bluetooth/net_bluetooth_mgmt.cpp b/bluetooth/net_bluetooth_mgmt.cpp new file mode 100644 index 0000000..24693ef --- /dev/null +++ b/bluetooth/net_bluetooth_mgmt.cpp @@ -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 +#include +#include +#include +#include + +#include +#include +#include +#include + +// Definitions imported from +#define BTPROTO_HCI 1 + +// Definitions imported from +#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 +#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 +#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(rfkill_bt_index_), + .type = RFKILL_TYPE_BLUETOOTH, + .op = RFKILL_OP_CHANGE, + .soft = static_cast(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(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 diff --git a/bluetooth/net_bluetooth_mgmt.h b/bluetooth/net_bluetooth_mgmt.h new file mode 100644 index 0000000..5c473f2 --- /dev/null +++ b/bluetooth/net_bluetooth_mgmt.h @@ -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 + +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 diff --git a/bluetooth/service.cpp b/bluetooth/service.cpp new file mode 100644 index 0000000..ef4b884 --- /dev/null +++ b/bluetooth/service.cpp @@ -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 +#include +#include +#include +#include + +#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 service = + ndk::SharedRefBase::make(); + 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; +}