mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-15 15:16:12 -04:00
More bluetooth medium code.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include <systemd/sd-bus-vtable.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_bluez_profile.h"
|
||||
#include "internal/platform/implementation/linux/bluez.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
const char *BLUEZ_PROFILEMANAGER_INTERFACE = "org.bluez.ProfileManager1";
|
||||
static int profile_release(sd_bus_message *m, void *userdata,
|
||||
sd_bus_error *error) {
|
||||
// TODO
|
||||
}
|
||||
static int profile_new_connection(sd_bus_message *m, void *userdata,
|
||||
sd_bus_error *error) {
|
||||
// TODO
|
||||
}
|
||||
static int profile_new(sd_bus_message *m, void *userdata, sd_bus_error *error) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
static const sd_bus_vtable vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD_WITH_ARGS("Release", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
|
||||
profile_release, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS(
|
||||
"NewConnection", SD_BUS_ARGS("o", path, "h", fd, "a{sq}", properties),
|
||||
SD_BUS_NO_RESULT, profile_new_connection, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("RequestDisconnection", SD_BUS_ARGS("o", object),
|
||||
SD_BUS_NO_RESULT, profile_new,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END};
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
std::unique_ptr<ProfileManager> NewProfileManager() {
|
||||
sd_bus *system_bus;
|
||||
if (auto ret = sd_bus_default_system(&system_bus); ret < 0) {
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
sd_bus_error_set_errno(&err, ret);
|
||||
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Error connecting to system bus: " << err.name << ": "
|
||||
<< err.message;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sd_bus_slot *slot = nullptr;
|
||||
auto manager = new ProfileManager(system_bus, slot);
|
||||
|
||||
if (auto ret = sd_bus_add_object_vtable(
|
||||
system_bus, &slot, "/com/github/google/nearby", "org.bluez.Profile1",
|
||||
vtable, manager->GetMethodData());
|
||||
ret < 0) {
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
sd_bus_error_set_errno(&err, ret);
|
||||
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Error adding object /com/github/google/nearby: "
|
||||
<< err.name << ": " << err.message;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<ProfileManager>(manager);
|
||||
}
|
||||
|
||||
bool ProfileManager::ProfileRegistered(absl::string_view service_uuid) {
|
||||
registered_service_uuids_lock_.ReaderLock();
|
||||
bool registered =
|
||||
registered_service_uuids_.count(std::string(service_uuid)) == 1;
|
||||
registered_service_uuids_lock_.ReaderUnlock();
|
||||
return registered;
|
||||
}
|
||||
|
||||
bool ProfileManager::RegisterProfile(absl::string_view service_uuid) {
|
||||
if (ProfileRegistered(service_uuid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
std::string uuid(service_uuid);
|
||||
|
||||
registered_service_uuids_lock_.Lock();
|
||||
if (sd_bus_call_method(system_bus_, BLUEZ_SERVICE, "/org/bluez",
|
||||
BLUEZ_PROFILEMANAGER_INTERFACE, "RegisterProfile", &err,
|
||||
nullptr, "osa{sq}", "/com/github/google/nearby",
|
||||
uuid.c_str(), 0, nullptr) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Error calling RegisterProfile: " << err.name << ": "
|
||||
<< err.message;
|
||||
registered_service_uuids_lock_.Unlock();
|
||||
return false;
|
||||
}
|
||||
registered_service_uuids_.insert(uuid);
|
||||
registered_service_uuids_lock_.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_BLUEZ_PROFILE_H_
|
||||
#define PLATFORM_IMPL_LINUX_BLUETOOTH_BLUEZ_PROFILE_H_
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
|
||||
class ProfileManager {
|
||||
public:
|
||||
ProfileManager(sd_bus *system_bus, sd_bus_slot *slot) {
|
||||
system_bus_ = system_bus;
|
||||
slot_ = slot;
|
||||
}
|
||||
~ProfileManager() { sd_bus_unref(system_bus_); }
|
||||
|
||||
bool ProfileRegistered(absl::string_view service_uuid);
|
||||
bool RegisterProfile(absl::string_view sevice_uuid);
|
||||
|
||||
std::optional<int> GetServiceRecordFD(api::BluetoothDevice &remote_device,
|
||||
absl::string_view service_uuid);
|
||||
|
||||
struct MethodData {
|
||||
std::map<std::tuple<std::tuple<std::string, std::string>>, int> &connections_;
|
||||
absl::Mutex &connections_lock_;
|
||||
};
|
||||
struct MethodData *GetMethodData() { return &data_; }
|
||||
|
||||
private:
|
||||
bool InitManagerObj();
|
||||
|
||||
// Maps (mac address, service uuid) tuples to FDs. Probably
|
||||
// an awful way to do this, but whatever.
|
||||
std::map<std::tuple<std::tuple<std::string, std::string>>, int> connections_;
|
||||
absl::Mutex connections_lock_;
|
||||
|
||||
MethodData data_{connections_, connections_lock_};
|
||||
|
||||
std::set<std::string> registered_service_uuids_;
|
||||
absl::Mutex registered_service_uuids_lock_;
|
||||
|
||||
sd_bus *system_bus_;
|
||||
sd_bus_slot *slot_;
|
||||
};
|
||||
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/substitute.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
||||
#include "internal/platform/implementation/linux/bluez.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
|
||||
BluetoothDevice::BluetoothDevice(absl::string_view adapter,
|
||||
absl::string_view address) {
|
||||
mac_addr_ = std::string(address);
|
||||
object_path_ = absl::Substitute("/org/bluez/$0/dev_$1", adapter,
|
||||
absl::StrReplaceAll(address, {{":", "_"}}));
|
||||
if (sd_bus_default_system(&system_bus) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error connecting to system bus";
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothDevice::BluetoothDevice(absl::string_view device_object_path) {
|
||||
if (sd_bus_default_system(&system_bus) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error connecting to system bus";
|
||||
return;
|
||||
}
|
||||
|
||||
object_path_ = device_object_path;
|
||||
}
|
||||
|
||||
std::string BluetoothDevice::GetName() const {
|
||||
if (!system_bus) {
|
||||
return std::string();
|
||||
}
|
||||
__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,
|
||||
object_path_.c_str(), BLUEZ_DEVICE_INTERFACE,
|
||||
"Alias", &err, &cname) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error getting alias for device "
|
||||
<< object_path_ << " :" << err.message;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string name(cname);
|
||||
free(cname);
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string BluetoothDevice::GetMacAddress() const {
|
||||
if (!system_bus) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (!mac_addr_.empty()) {
|
||||
return mac_addr_;
|
||||
}
|
||||
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
char *c_addr = nullptr;
|
||||
if (sd_bus_get_property_string(system_bus, BLUEZ_SERVICE,
|
||||
object_path_.c_str(), BLUEZ_DEVICE_INTERFACE,
|
||||
"Address", &err, &c_addr) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error getting address for device "
|
||||
<< object_path_ << " :" << err.message;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string addr(c_addr);
|
||||
free(c_addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_DEVICE_H_
|
||||
#define PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_DEVICE_H_
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
const char *BLUEZ_DEVICE_INTERFACE = "org.bluez.Device1";
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html.
|
||||
class BluetoothDevice : public api::BluetoothDevice {
|
||||
public:
|
||||
BluetoothDevice(absl::string_view adapter, absl::string_view address);
|
||||
BluetoothDevice(absl::string_view device_object_path);
|
||||
|
||||
virtual ~BluetoothDevice() override { sd_bus_unref(system_bus); };
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName()
|
||||
std::string GetName() const override;
|
||||
|
||||
// Returns BT MAC address assigned to this device.
|
||||
std::string GetMacAddress() const override;
|
||||
|
||||
private:
|
||||
sd_bus *system_bus;
|
||||
std::string object_path_;
|
||||
std::string mac_addr_;
|
||||
};
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
#include <cstring>
|
||||
|
||||
#include <memory>
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/substitute.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_medium.h"
|
||||
#include "internal/platform/implementation/linux/bluez.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
|
||||
int bluez_interfaces_added_signal_handler(sd_bus_message *m, void *userdata,
|
||||
sd_bus_error *ret_error) {
|
||||
const sd_bus_error *reply_err = sd_bus_message_get_error(m);
|
||||
if (reply_err) {
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Received error while listening for InterfacesAdded: "
|
||||
<< reply_err->message;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct BluetoothClassicMedium::DiscoveryParams *params =
|
||||
static_cast<BluetoothClassicMedium::DiscoveryParams *>(userdata);
|
||||
char *c_object_path = nullptr;
|
||||
int ret = sd_bus_message_read(m, "o", &c_object_path);
|
||||
if (ret < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Error reading object path from message: " << ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string object_path(c_object_path);
|
||||
|
||||
if (!absl::StrContains(object_path, absl::StrCat(params->adapter_object_path,
|
||||
"/", "dev_"))) {
|
||||
// Interface added for an object we dont care about.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (params->devices_by_path.count(object_path) != 0) {
|
||||
// Object already exists
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = sd_bus_message_enter_container(m, 'a', "{sa{sv}}");
|
||||
if (ret < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error entering container: " << ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const char *interface_name = nullptr;
|
||||
ret = sd_bus_message_read(m, "s", &interface_name);
|
||||
if (ret < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error reading dict entry: " << ret;
|
||||
}
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
if (strcmp(interface_name, "org.bluez.Device1") == 0) {
|
||||
NEARBY_LOGS(INFO) << __func__ << "Encountered new device at "
|
||||
<< c_object_path;
|
||||
auto bluetoothDevice =
|
||||
std::make_unique<BluetoothDevice>(BluetoothDevice(object_path));
|
||||
params->devices_by_path[object_path] = std::move(bluetoothDevice);
|
||||
|
||||
if (params->cb.device_discovered_cb != nullptr) {
|
||||
params->cb.device_discovered_cb(*params->devices_by_path[object_path]);
|
||||
}
|
||||
for (auto &observer : params->observers_.GetObservers()) {
|
||||
observer->DeviceAdded(*params->devices_by_path[object_path]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BluetoothClassicMedium::BluetoothClassicMedium(absl::string_view adapter) {
|
||||
if (sd_bus_default_system(&system_bus_) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error connecting to system bus";
|
||||
}
|
||||
adapter_object_path_ = absl::Substitute("/org/bluez/$0/", adapter);
|
||||
}
|
||||
|
||||
BluetoothClassicMedium::~BluetoothClassicMedium() {
|
||||
if (system_bus_)
|
||||
sd_bus_unref(system_bus_);
|
||||
if (system_bus_slot_)
|
||||
sd_bus_slot_unref(system_bus_slot_);
|
||||
}
|
||||
|
||||
bool BluetoothClassicMedium::StartDiscovery(
|
||||
DiscoveryCallback discovery_callback) {
|
||||
if (!system_bus_)
|
||||
return false;
|
||||
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
__attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply =
|
||||
nullptr;
|
||||
|
||||
discovery_params_.cb = std::move(discovery_callback);
|
||||
discovery_params_.adapter_object_path = adapter_object_path_;
|
||||
|
||||
sd_bus_match_signal(system_bus_, &system_bus_slot_, BLUEZ_SERVICE, "/",
|
||||
"org.freedesktop.DBus.ObjectManager", "InterfacesAdded",
|
||||
bluez_interfaces_added_signal_handler,
|
||||
&discovery_params_);
|
||||
|
||||
if (sd_bus_call_method(system_bus_, BLUEZ_SERVICE,
|
||||
adapter_object_path_.c_str(), BLUEZ_ADAPTER_INTERFACE,
|
||||
"StartDiscovery", &err, &reply, nullptr) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error calling StartDiscovery on adapter "
|
||||
<< adapter_object_path_ << ": " << err.message;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothClassicMedium::StopDiscovery() {
|
||||
if (!system_bus_)
|
||||
return false;
|
||||
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
__attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply =
|
||||
nullptr;
|
||||
|
||||
int ret = sd_bus_call_method(
|
||||
system_bus_, BLUEZ_SERVICE, adapter_object_path_.c_str(),
|
||||
BLUEZ_ADAPTER_INTERFACE, "StopDiscovery", &err, &reply, nullptr);
|
||||
if (ret < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error calling StopDiscovery on "
|
||||
<< adapter_object_path_ << ": " << err.message;
|
||||
return false;
|
||||
}
|
||||
const sd_bus_error *m_err = sd_bus_message_get_error(reply);
|
||||
|
||||
if (m_err) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error calling StopDiscovery on "
|
||||
<< adapter_object_path_ << ": " << err.message;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::BluetoothSocket>
|
||||
BluetoothClassicMedium::ConnectToService(api::BluetoothDevice &remote_device,
|
||||
const std::string &service_uuid,
|
||||
CancellationFlag *cancellation_flag) {
|
||||
auto device_object_path = GetDeviceObjectPath(remote_device.GetMacAddress());
|
||||
|
||||
}
|
||||
|
||||
std::string
|
||||
BluetoothClassicMedium::GetDeviceObjectPath(absl::string_view mac_address) {
|
||||
return absl::Substitute("$0/dev_$1", adapter_object_path_, absl::StrReplaceAll(mac_address, {{":", "_"}}));
|
||||
}
|
||||
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_MEDIUM_H_
|
||||
#define PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_MEDIUM_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "internal/base/observer_list.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
// Container of operations that can be performed over the Bluetooth Classic
|
||||
// medium.
|
||||
class BluetoothClassicMedium : public api::BluetoothClassicMedium {
|
||||
public:
|
||||
BluetoothClassicMedium(absl::string_view adapter);
|
||||
~BluetoothClassicMedium();
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery()
|
||||
//
|
||||
// Returns true once the process of discovery has been initiated.
|
||||
bool StartDiscovery(DiscoveryCallback discovery_callback) override;
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#cancelDiscovery()
|
||||
//
|
||||
// Returns true once discovery is well and truly stopped; after this returns,
|
||||
// there must be no more invocations of the DiscoveryCallback passed in to
|
||||
// StartDiscovery().
|
||||
bool StopDiscovery() override;
|
||||
|
||||
// A combination of
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureRfcommSocketToServiceRecord
|
||||
// followed by
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#connect().
|
||||
//
|
||||
// service_uuid is the canonical textual representation
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
|
||||
// type 3 name-based
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
|
||||
// UUID.
|
||||
//
|
||||
// On success, returns a new BluetoothSocket.
|
||||
// On error, returns nullptr.
|
||||
std::unique_ptr<api::BluetoothSocket>
|
||||
ConnectToService(api::BluetoothDevice &remote_device,
|
||||
const std::string &service_uuid,
|
||||
CancellationFlag *cancellation_flag) override;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord
|
||||
//
|
||||
// service_uuid is the canonical textual representation
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
|
||||
// type 3 name-based
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
|
||||
// UUID.
|
||||
//
|
||||
// Returns nullptr error.
|
||||
std::unique_ptr<api::BluetoothServerSocket>
|
||||
ListenForService(const std::string &service_name,
|
||||
const std::string &service_uuid) override;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond()
|
||||
//
|
||||
// Start the bonding (pairing) process with the remote device.
|
||||
// Return a Bluetooth pairing instance to handle the pairing process with the
|
||||
// remote device.
|
||||
std::unique_ptr<api::BluetoothPairing>
|
||||
CreatePairing(api::BluetoothDevice &remote_device) override;
|
||||
|
||||
api::BluetoothDevice *
|
||||
GetRemoteDevice(const std::string &mac_address) override;
|
||||
|
||||
void AddObserver(Observer *observer) override;
|
||||
void RemoveObserver(Observer *observer) override;
|
||||
|
||||
struct DiscoveryParams {
|
||||
std::string &adapter_object_path;
|
||||
std::map<std::string, std::unique_ptr<BluetoothDevice>> &devices_by_path;
|
||||
ObserverList<Observer> &observers_;
|
||||
BluetoothClassicMedium::DiscoveryCallback cb;
|
||||
};
|
||||
|
||||
private:
|
||||
std::string GetDeviceObjectPath(absl::string_view mac_address);
|
||||
|
||||
sd_bus *system_bus_ = nullptr;
|
||||
sd_bus_slot *system_bus_slot_ = nullptr;
|
||||
std::string adapter_object_path_ = std::string();
|
||||
std::map<std::string, std::unique_ptr<BluetoothDevice>> devices_by_id_;
|
||||
ObserverList<Observer> observers_;
|
||||
|
||||
DiscoveryParams discovery_params_ = {adapter_object_path_, devices_by_id_, observers_};
|
||||
};
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_SERVER_SOCKET_H_
|
||||
#define PLATFORM_IMPL_LINUX_BLUETOOTH_SERVER_SOCKET_H_
|
||||
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/exception.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
class BluetoothServerSocket : api::BluetoothServerSocket {
|
||||
public:
|
||||
~BluetoothServerSocket() = default;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept()
|
||||
//
|
||||
// Blocks until either:
|
||||
// - at least one incoming connection request is available, or
|
||||
// - ServerSocket is closed.
|
||||
// On success, returns connected socket, ready to exchange data.
|
||||
// Returns nullptr on error.
|
||||
// Once error is reported, it is permanent, and ServerSocket has to be
|
||||
// closed.
|
||||
std::unique_ptr<api::BluetoothSocket> Accept() override;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close()
|
||||
//
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
Exception Close() override;
|
||||
};
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_socket.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
|
||||
ExceptionOr<ByteArray> BluetoothInputStream::Read(std::int64_t size) {
|
||||
char *data = new char[size];
|
||||
ssize_t ret = read(fd_, data, size);
|
||||
if (ret == 0) {
|
||||
delete[] data;
|
||||
return ExceptionOr(ByteArray());
|
||||
} else if (ret < 0) {
|
||||
delete[] data;
|
||||
return Exception::kIo;
|
||||
}
|
||||
|
||||
return ExceptionOr(ByteArray(data, size));
|
||||
}
|
||||
|
||||
ExceptionOr<std::size_t> BluetoothInputStream::Skip(std::size_t offset) {
|
||||
auto off = lseek(fd_, offset, SEEK_CUR);
|
||||
if (off != offset) {
|
||||
auto end = lseek(fd_, 0, SEEK_END);
|
||||
return off == end ? ExceptionOr((std::size_t)off) : Exception::kIo;
|
||||
}
|
||||
return ExceptionOr((std::size_t)(off));
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> BluetoothInputStream::ReadExactly(std::size_t size) {
|
||||
char *data = new char[size];
|
||||
ssize_t ret = read(fd_, data, size);
|
||||
if (ret < 0) {
|
||||
delete[] data;
|
||||
return Exception::kIo;
|
||||
}
|
||||
|
||||
return ExceptionOr(ByteArray(data, size));
|
||||
}
|
||||
|
||||
Exception BluetoothOutputStream::Write(const ByteArray &data) {
|
||||
ssize_t written = 0;
|
||||
while (written < data.size()) {
|
||||
ssize_t ret = write(fd_, data.data(), data.size());
|
||||
if (ret < 1) {
|
||||
return Exception{Exception::kIo};
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
return Exception{Exception::kSuccess};
|
||||
}
|
||||
|
||||
Exception BluetoothOutputStream::Flush() {
|
||||
return Exception{Exception::kSuccess};
|
||||
}
|
||||
|
||||
Exception BluetoothOutputStream::Close() {
|
||||
return close(fd_) < 0 ? Exception{Exception::kIo}
|
||||
: Exception{Exception::kSuccess};
|
||||
}
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_SOCKET_H_
|
||||
#define PLATFORM_IMPL_LINUX_BLUETOOTH_SOCKET_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
|
||||
class BluetoothInputStream : public InputStream {
|
||||
public:
|
||||
BluetoothInputStream(int fd) { fd_ = fd; };
|
||||
|
||||
ExceptionOr<ByteArray> Read(std::int64_t size) override;
|
||||
ExceptionOr<size_t> Skip(size_t offset) override;
|
||||
ExceptionOr<ByteArray> ReadExactly(std::size_t size);
|
||||
|
||||
Exception Close() override;
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
class BluetoothOutputStream : public OutputStream {
|
||||
public:
|
||||
BluetoothOutputStream(int fd) { fd_ = fd; };
|
||||
|
||||
Exception Write(const ByteArray &data) override;
|
||||
Exception Flush() override;
|
||||
Exception Close() override;
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
class BluetoothSocket : public api::BluetoothSocket {
|
||||
public:
|
||||
BluetoothSocket(std::string object, int fd) {
|
||||
fd_ = fd;
|
||||
object_ = object;
|
||||
input_stream_ = BluetoothInputStream(fd_);
|
||||
output_stream_ = BluetoothOutputStream(fd_);
|
||||
}
|
||||
|
||||
InputStream &GetInputStream() override { return input_stream_; }
|
||||
OutputStream &GetOutputStream() override { return output_stream_; }
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
std::string object_;
|
||||
BluetoothInputStream input_stream_ = {-1};
|
||||
BluetoothOutputStream output_stream_ = {-1};
|
||||
};
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <utility>
|
||||
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_pairing.h"
|
||||
#include "internal/platform/implementation/linux/bluez.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
|
||||
int pairing_reply_handler(sd_bus_message *m, void *userdata,
|
||||
sd_bus_error *err) {
|
||||
auto pairing_cb = static_cast<api::BluetoothPairingCallback *>(userdata);
|
||||
if (sd_bus_message_is_method_error(m, nullptr)) {
|
||||
if (sd_bus_message_is_method_error(
|
||||
m, "org.bluez.Error.AuthenticationCanceled")) {
|
||||
pairing_cb->on_pairing_error_cb(
|
||||
api::BluetoothPairingCallback::PairingError::kAuthCanceled);
|
||||
} else if (sd_bus_message_is_method_error(
|
||||
m, "org.bluez.Error.AuthenticationFailed")) {
|
||||
pairing_cb->on_pairing_error_cb(
|
||||
api::BluetoothPairingCallback::PairingError::kAuthFailed);
|
||||
} else if (sd_bus_message_is_method_error(
|
||||
m, "org.bluez.Error.AuthenticationRejected")) {
|
||||
pairing_cb->on_pairing_error_cb(
|
||||
api::BluetoothPairingCallback::PairingError::kAuthRejected);
|
||||
} else if (sd_bus_message_is_method_error(
|
||||
m, "org.bluez.Error.AuthenticationTimeout")) {
|
||||
pairing_cb->on_pairing_error_cb(
|
||||
api::BluetoothPairingCallback::PairingError::kAuthTimeout);
|
||||
} else {
|
||||
pairing_cb->on_pairing_error_cb(
|
||||
api::BluetoothPairingCallback::PairingError::kAuthFailed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (err) {
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Error pairing with device: " << err->message;
|
||||
pairing_cb->on_pairing_error_cb(
|
||||
api::BluetoothPairingCallback::PairingError::kUnknown);
|
||||
} else {
|
||||
pairing_cb->on_paired_cb();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BluetoothPairing::BluetoothPairing(absl::string_view object_path) {
|
||||
object_path_ = object_path;
|
||||
if (sd_bus_default_system(&system_bus_) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error connecting to system bus";
|
||||
}
|
||||
}
|
||||
|
||||
bool BluetoothPairing::InitiatePairing(
|
||||
api::BluetoothPairingCallback pairing_cb) {
|
||||
if (!system_bus_)
|
||||
return false;
|
||||
|
||||
pairing_cb_ = std::move(pairing_cb);
|
||||
|
||||
if (sd_bus_call_method_async(system_bus_, nullptr, BLUEZ_SERVICE,
|
||||
object_path_.c_str(), BLUEZ_DEVICE_INTERFACE,
|
||||
"Pair", &pairing_reply_handler, &pairing_cb_,
|
||||
nullptr) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << "Error calling method Pair on device "
|
||||
<< object_path_;
|
||||
return false;
|
||||
}
|
||||
pairing_cb.on_pairing_initiated_cb(api::PairingParams{
|
||||
api::PairingParams::PairingType::kConsent, std::string()});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothPairing::FinishPairing(
|
||||
std::optional<absl::string_view> pin_code) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothPairing::CancelPairing() {
|
||||
if (!system_bus_)
|
||||
return false;
|
||||
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
if (sd_bus_call_method(system_bus_, BLUEZ_SERVICE, object_path_.c_str(),
|
||||
BLUEZ_DEVICE_INTERFACE, "CancelPairing", &err, nullptr,
|
||||
nullptr)) {
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Error calling method CancelPairing on device "
|
||||
<< object_path_ << ": " << err.message;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothPairing::Unpair() {
|
||||
if (!system_bus_)
|
||||
return false;
|
||||
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
if (sd_bus_call_method(system_bus_, BLUEZ_SERVICE, "/org/bluez/hci0",
|
||||
BLUEZ_ADAPTER_INTERFACE, "RemoveDevice", &err, nullptr,
|
||||
"o", object_path_.c_str())) {
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Error calling method CancelPairing on device "
|
||||
<< object_path_ << ": " << err.message;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothPairing::IsPaired() {
|
||||
if (!system_bus_)
|
||||
return false;
|
||||
|
||||
__attribute__((cleanup(sd_bus_error_free))) sd_bus_error err =
|
||||
SD_BUS_ERROR_NULL;
|
||||
int paired = 0;
|
||||
if (sd_bus_get_property_trivial(system_bus_, BLUEZ_SERVICE,
|
||||
object_path_.c_str(), BLUEZ_DEVICE_INTERFACE,
|
||||
"Bonded", &err, 'b', &paired) < 0) {
|
||||
NEARBY_LOGS(ERROR) << __func__
|
||||
<< "Error getting Bonded property for device "
|
||||
<< object_path_ << ": " << err.message;
|
||||
}
|
||||
return paired;
|
||||
}
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_PROFILE_H_
|
||||
#define PLATFORM_IMPL_LINUX_BLUETOOTH_PAIRING_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
class BluetoothPairing : public api::BluetoothPairing {
|
||||
public:
|
||||
BluetoothPairing(absl::string_view object_path);
|
||||
~BluetoothPairing() { sd_bus_unref(system_bus_); }
|
||||
|
||||
bool InitiatePairing(api::BluetoothPairingCallback pairing_cb) override;
|
||||
bool FinishPairing(std::optional<absl::string_view> pin_code) override;
|
||||
bool CancelPairing() override;
|
||||
bool Unpair() override;
|
||||
bool IsPaired() override;
|
||||
|
||||
private:
|
||||
std::string object_path_;
|
||||
sd_bus *system_bus_;
|
||||
api::BluetoothPairingCallback pairing_cb_;
|
||||
};
|
||||
} // namespace linux
|
||||
} // namespace nearby
|
||||
|
||||
#endif
|
||||
@@ -4,7 +4,6 @@
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
const char *BLUEZ_SERVICE = "org.bluez";
|
||||
|
||||
const char *BLUEZ_ADAPTER_INTERFACE = "org.bluez.Adapter1";
|
||||
|
||||
} // namespace linux
|
||||
|
||||
Reference in New Issue
Block a user