Bluetooth HAL: Add an AIDL default implementation
Bug: 205758693
Test: VtsHalBluetoothTargetTest
Change-Id: Ie8839e38448a309f2e6616d852026066b6c6c064
diff --git a/bluetooth/aidl/default/Android.bp b/bluetooth/aidl/default/Android.bp
new file mode 100644
index 0000000..d1761f5
--- /dev/null
+++ b/bluetooth/aidl/default/Android.bp
@@ -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",
+ ],
+ },
+}
diff --git a/bluetooth/aidl/default/BluetoothHci.cpp b/bluetooth/aidl/default/BluetoothHci.cpp
new file mode 100644
index 0000000..d0ad4a3
--- /dev/null
+++ b/bluetooth/aidl/default/BluetoothHci.cpp
@@ -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
diff --git a/bluetooth/aidl/default/BluetoothHci.h b/bluetooth/aidl/default/BluetoothHci.h
new file mode 100644
index 0000000..0ed0623
--- /dev/null
+++ b/bluetooth/aidl/default/BluetoothHci.h
@@ -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
diff --git a/bluetooth/aidl/default/bluetooth-service-default.rc b/bluetooth/aidl/default/bluetooth-service-default.rc
new file mode 100644
index 0000000..1841c77
--- /dev/null
+++ b/bluetooth/aidl/default/bluetooth-service-default.rc
@@ -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
diff --git a/bluetooth/aidl/default/bluetooth-service-default.xml b/bluetooth/aidl/default/bluetooth-service-default.xml
new file mode 100644
index 0000000..bb05995
--- /dev/null
+++ b/bluetooth/aidl/default/bluetooth-service-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.bluetooth</name>
+ <fqname>IBluetoothHci/default</fqname>
+ </hal>
+</manifest>
diff --git a/bluetooth/aidl/default/service.cpp b/bluetooth/aidl/default/service.cpp
new file mode 100644
index 0000000..9af2a08
--- /dev/null
+++ b/bluetooth/aidl/default/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 <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;
+}
diff --git a/bluetooth/aidl/default/test/fuzzer.cpp b/bluetooth/aidl/default/test/fuzzer.cpp
new file mode 100644
index 0000000..e7a1eef
--- /dev/null
+++ b/bluetooth/aidl/default/test/fuzzer.cpp
@@ -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;
+}