diff options
-rw-r--r-- | bluetooth/aidl/default/Android.bp | 8 | ||||
-rw-r--r-- | bluetooth/aidl/default/BluetoothHci.cpp | 57 | ||||
-rw-r--r-- | bluetooth/aidl/default/BluetoothHci.h | 3 | ||||
-rw-r--r-- | bluetooth/aidl/default/bluetooth-service-default.rc | 2 | ||||
-rw-r--r-- | bluetooth/aidl/default/net_bluetooth_mgmt.cpp | 296 | ||||
-rw-r--r-- | bluetooth/aidl/default/net_bluetooth_mgmt.h | 47 |
6 files changed, 395 insertions, 18 deletions
diff --git a/bluetooth/aidl/default/Android.bp b/bluetooth/aidl/default/Android.bp index d1761f5cae..3f4ba9903a 100644 --- a/bluetooth/aidl/default/Android.bp +++ b/bluetooth/aidl/default/Android.bp @@ -30,6 +30,7 @@ cc_library_static { defaults: ["android.hardware.bluetooth-service-build-defaults"], srcs: [ "BluetoothHci.cpp", + "net_bluetooth_mgmt.cpp", ], } @@ -37,7 +38,7 @@ cc_binary { name: "android.hardware.bluetooth-service.default", relative_install_path: "hw", init_rc: ["bluetooth-service-default.rc"], - vintf_fragments: ["bluetooth-service-default.xml"], + vintf_fragments: [":manifest_android.hardware.bluetooth-service.default.xml"], vendor: true, defaults: ["android.hardware.bluetooth-service-build-defaults"], srcs: [ @@ -77,3 +78,8 @@ cc_fuzz { ], }, } + +filegroup { + name: "manifest_android.hardware.bluetooth-service.default.xml", + srcs: ["bluetooth-service-default.xml"], +} diff --git a/bluetooth/aidl/default/BluetoothHci.cpp b/bluetooth/aidl/default/BluetoothHci.cpp index 4d4896d162..dd102a1596 100644 --- a/bluetooth/aidl/default/BluetoothHci.cpp +++ b/bluetooth/aidl/default/BluetoothHci.cpp @@ -44,6 +44,7 @@ int SetTerminalRaw(int fd) { using namespace ::android::hardware::bluetooth::hci; using namespace ::android::hardware::bluetooth::async; +using aidl::android::hardware::bluetooth::Status; namespace aidl::android::hardware::bluetooth::impl { @@ -97,21 +98,25 @@ BluetoothHci::BluetoothHci(const std::string& dev_path) { 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) { +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 ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE); + return fd; } 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); + ::close(fd); + return -1; } + return fd; +} + +ndk::ScopedAStatus BluetoothHci::initialize( + const std::shared_ptr<IBluetoothHciCallbacks>& cb) { + ALOGI(__func__); mCb = cb; if (mCb == nullptr) { @@ -119,16 +124,20 @@ ndk::ScopedAStatus BluetoothHci::initialize( return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE); } - mDeathRecipient->LinkToDeath(mCb); + management_.reset(new NetBluetoothMgmt); + mFd = management_->openHci(); + if (mFd < 0) { + management_.reset(); - auto init_ret = cb->initializationComplete(Status::SUCCESS); - if (!init_ret.isOk()) { - if (!mDeathRecipient->getHasDied()) { - ALOGE("Error sending init callback, but no death notification."); + ALOGI("Unable to open Linux interface, trying default path."); + mFd = getFdFromDevPath(); + if (mFd < 0) { + return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE); } - return ndk::ScopedAStatus::fromServiceSpecificError( - STATUS_FAILED_TRANSACTION); } + + mDeathRecipient->LinkToDeath(mCb); + mH4 = std::make_shared<H4Protocol>( mFd, [](const std::vector<uint8_t>& /* raw_command */) { @@ -152,13 +161,29 @@ ndk::ScopedAStatus BluetoothHci::initialize( }); mFdWatcher.WatchFdForNonBlockingReads(mFd, [this](int) { mH4->OnDataReady(); }); + + 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__); mFdWatcher.StopWatchingFileDescriptors(); - ::close(mFd); + if (management_) { + management_->closeHci(); + } else { + ::close(mFd); + } return ndk::ScopedAStatus::ok(); } diff --git a/bluetooth/aidl/default/BluetoothHci.h b/bluetooth/aidl/default/BluetoothHci.h index 0ed0623e47..a0908f840c 100644 --- a/bluetooth/aidl/default/BluetoothHci.h +++ b/bluetooth/aidl/default/BluetoothHci.h @@ -24,6 +24,7 @@ #include "async_fd_watcher.h" #include "h4_protocol.h" +#include "net_bluetooth_mgmt.h" namespace aidl::android::hardware::bluetooth::impl { @@ -64,8 +65,10 @@ class BluetoothHci : public BnBluetoothHci { ::android::hardware::bluetooth::async::AsyncFdWatcher mFdWatcher; + int getFdFromDevPath(); void send(::android::hardware::bluetooth::hci::PacketType type, const std::vector<uint8_t>& packet); + std::unique_ptr<NetBluetoothMgmt> management_{}; }; } // namespace aidl::android::hardware::bluetooth::impl diff --git a/bluetooth/aidl/default/bluetooth-service-default.rc b/bluetooth/aidl/default/bluetooth-service-default.rc index 1841c77750..dc7869873c 100644 --- a/bluetooth/aidl/default/bluetooth-service-default.rc +++ b/bluetooth/aidl/default/bluetooth-service-default.rc @@ -1,4 +1,4 @@ -service bluetooth_hal_service /vendor/bin/hw/android.hardware.bluetooth-service.default +service vendor.bluetooth-default /vendor/bin/hw/android.hardware.bluetooth-service.default class hal capabilities BLOCK_SUSPEND NET_ADMIN SYS_NICE user bluetooth diff --git a/bluetooth/aidl/default/net_bluetooth_mgmt.cpp b/bluetooth/aidl/default/net_bluetooth_mgmt.cpp new file mode 100644 index 0000000000..937cd57432 --- /dev/null +++ b/bluetooth/aidl/default/net_bluetooth_mgmt.cpp @@ -0,0 +1,296 @@ +/* + * 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; + + for (int i = 0; i < data->num_controllers; i++) { + if (data->index[i] == hci_interface) { + ALOGI("hci interface %d found", hci_interface); + ret = 0; + 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 = 0; + 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. + if (waitHciDev(hci_interface)) { + 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_interface), + .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_interface); + 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/aidl/default/net_bluetooth_mgmt.h b/bluetooth/aidl/default/net_bluetooth_mgmt.h new file mode 100644 index 0000000000..5c473f2e0b --- /dev/null +++ b/bluetooth/aidl/default/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 <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 |