diff --git a/internal/platform/implementation/linux/BUILD b/internal/platform/implementation/linux/BUILD index 5eb73e71..c57cff94 100644 --- a/internal/platform/implementation/linux/BUILD +++ b/internal/platform/implementation/linux/BUILD @@ -16,3 +16,17 @@ cc_library( ], ) +cc_library( + name = "linux", + hdrs = [ + "bluetooth_adapter.h", + "bluez.h" + ], + srcs = ["bluetooth_adapter.cc"], + deps = [ + "//internal/platform/implementation:types", + "//internal/platform:logging", + "@com_google_absl//absl/strings", + "@libsystemd//:lib", + ] +) diff --git a/internal/platform/implementation/linux/bluetooth_adapter.cc b/internal/platform/implementation/linux/bluetooth_adapter.cc new file mode 100644 index 00000000..cc734974 --- /dev/null +++ b/internal/platform/implementation/linux/bluetooth_adapter.cc @@ -0,0 +1,161 @@ +#include + +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace linux { + +using namespace api; + +bool BluetoothAdapter::SetStatus(Status status) { + __attribute__((cleanup(sd_bus_error_free))) sd_bus_error err = + SD_BUS_ERROR_NULL; + + if (sd_bus_set_property( + system_bus, BLUEZ_SERVICE, "/org/bluez/hci0", BLUEZ_ADAPTER_INTERFACE, + "Powered", &err, "b", + status == api::BluetoothAdapter::Status::kEnabled ? 1 : 0) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error setting adaptor status: " << err.message; + return false; + } + return true; +} + +bool BluetoothAdapter::IsEnabled() const { + __attribute__((cleanup(sd_bus_error_free))) sd_bus_error err = + SD_BUS_ERROR_NULL; + int enabled = 0; + + if (sd_bus_get_property_trivial(system_bus, BLUEZ_SERVICE, "/org/bluez/hci0", + BLUEZ_ADAPTER_INTERFACE, "Powered", &err, 'b', + &enabled) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error getting adaptor status: " << err.message; + } + return enabled; +} + +BluetoothAdapter::ScanMode BluetoothAdapter::GetScanMode() const { + __attribute__((cleanup(sd_bus_error_free))) sd_bus_error err = + SD_BUS_ERROR_NULL; + int powered = 0; + int discoverable = 0; + + if (sd_bus_get_property_trivial(system_bus, BLUEZ_SERVICE, "/org/bluez/hci0", + BLUEZ_ADAPTER_INTERFACE, "Powered", &err, 'b', + &powered) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error getting adaptor status: " << err.message; + return ScanMode::kUnknown; + } + if (!powered) { + return ScanMode::kNone; + } + + if (sd_bus_get_property_trivial(system_bus, BLUEZ_SERVICE, "/org/bluez/hci0", + BLUEZ_ADAPTER_INTERFACE, "Discoverable", &err, + 'b', &powered) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error getting adaptor's discoverable status: " + << err.message; + return ScanMode::kUnknown; + } + return discoverable ? ScanMode::kConnectableDiscoverable + : ScanMode::kConnectable; +} + +bool BluetoothAdapter::SetScanMode(ScanMode scan_mode) { + switch (scan_mode) { + case ScanMode::kConnectable: + return SetStatus(Status::kEnabled); + case ScanMode::kConnectableDiscoverable: { + if (!SetStatus(Status::kEnabled)) { + return false; + } + __attribute__((cleanup(sd_bus_error_free))) sd_bus_error err = + SD_BUS_ERROR_NULL; + if (sd_bus_set_property(system_bus, BLUEZ_SERVICE, "/org/bluez/hci0", + BLUEZ_ADAPTER_INTERFACE, "Discoverable", &err, "b", + 1) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error setting adapter's discoverable status: " + << err.message; + return false; + } + return true; + } + case ScanMode::kNone: + return SetStatus(Status::kDisabled); + default: + return false; + } +} + +std::string BluetoothAdapter::GetName() const { + __attribute__((cleanup(sd_bus_error_free))) sd_bus_error err = + SD_BUS_ERROR_NULL; + char *cname = nullptr; + if (sd_bus_get_property_string(system_bus, BLUEZ_SERVICE, "/org/bluez/hci0", + BLUEZ_ADAPTER_INTERFACE, "Alias", &err, + &cname) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error getting adapter's name: " << err.message; + return std::string(); + } + std::string name(cname); + free(cname); + return name; +} + +bool BluetoothAdapter::SetName(absl::string_view name, bool persist) { + if (persist) { + __attribute__((cleanup(sd_bus_error_free))) sd_bus_error err = + SD_BUS_ERROR_NULL; + std::string pretty_hostname(name); + if (sd_bus_set_property(system_bus, "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", "PrettyHostname", &err, + "s", pretty_hostname.c_str()) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error setting PrettyHostname: " << err.message; + } + } + return SetName(name); +} + +bool BluetoothAdapter::SetName(absl::string_view name) { + std::string alias(name); + __attribute__((cleanup(sd_bus_error_free))) sd_bus_error err = + SD_BUS_ERROR_NULL; + if (sd_bus_set_property(system_bus, BLUEZ_SERVICE, "/org/bluez/hci0", + BLUEZ_ADAPTER_INTERFACE, "Alias", &err, "s", + alias.c_str()) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error setting adapter's name: " << err.message; + return false; + } + return true; +} + +std::string BluetoothAdapter::GetMacAddress() const { + __attribute__((cleanup(sd_bus_error_free))) sd_bus_error err = + SD_BUS_ERROR_NULL; + char *caddr = nullptr; + if (sd_bus_get_property_string(system_bus, BLUEZ_SERVICE, "/org/bluez/hci0", + BLUEZ_ADAPTER_INTERFACE, "Address", &err, + &caddr) < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Error getting adapter's name: " << err.message; + return std::string(); + } + std::string addr(caddr); + free(caddr); + return addr; +} + +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/bluetooth_adapter.h b/internal/platform/implementation/linux/bluetooth_adapter.h new file mode 100644 index 00000000..0ca0facf --- /dev/null +++ b/internal/platform/implementation/linux/bluetooth_adapter.h @@ -0,0 +1,38 @@ +#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_ADAPTER_H_ +#define PLATFORM_IMPL_LINUX_BLUETOOTH_ADAPTER_H_ + +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include + +namespace nearby { +namespace linux { +class BluetoothAdapter : public api::BluetoothAdapter { +public: + ~BluetoothAdapter() override { + if (system_bus) { + sd_bus_unrefp(&system_bus); + } + }; + + bool SetStatus(Status status) override; + bool IsEnabled() const override; + + ScanMode GetScanMode() const override; + + bool SetScanMode(ScanMode scan_mode) override; + std::string GetName() const override; + + bool SetName(absl::string_view name) override; + bool SetName(absl::string_view name, bool persist) override; + std::string GetMacAddress() const override; + +private: + sd_bus *system_bus; +}; +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_BLUETOOTH_ADAPTER_H_ diff --git a/internal/platform/implementation/linux/bluez.h b/internal/platform/implementation/linux/bluez.h new file mode 100644 index 00000000..472885a1 --- /dev/null +++ b/internal/platform/implementation/linux/bluez.h @@ -0,0 +1,13 @@ +#ifndef PLATFORM_IMPL_LINUX_BLUEZ_H_ +#define PLATFORM_IMPL_LINUX_BLUEZ_H_ + +namespace nearby { +namespace linux { +const char *BLUEZ_SERVICE = "org.bluez"; + +const char *BLUEZ_ADAPTER_INTERFACE = "org.bluez.Adapter1"; + +} // namespace linux +} // namespace nearby + +#endif