Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
5 changes: 3 additions & 2 deletions cpp/windows/.gitignore → cpp/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
# *.ipr

# CMake
cmake-build-*/
windows/cmake-build-debug-visual-studio/
linux/cmake-build-debug-linux-machine/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml
Expand Down Expand Up @@ -83,4 +84,4 @@ http-client.private.env.json
.idea/ApifoxUploaderProjectSetting.xml

# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215
.idea/**/copilot.data.migration.*.xml
.idea/**/copilot.data.migration.*.xml
6 changes: 6 additions & 0 deletions cpp/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 4.2)
project(linux_ble)

set(CMAKE_CXX_STANDARD 20)

add_subdirectory(bt_common)
91 changes: 91 additions & 0 deletions cpp/linux/bt_common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
cmake_minimum_required(VERSION 4.2)
project(bt_common LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0 gio-2.0)

include(FetchContent)
FetchContent_Declare(
cpptrace
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
GIT_TAG v1.0.4
)

FetchContent_Declare(
bluez_inc
GIT_REPOSITORY https://github.com/weliem/bluez_inc
GIT_TAG main
)

FetchContent_Declare(
plog
GIT_REPOSITORY https://github.com/SergiusTheBest/plog
GIT_TAG 1.1.10
GIT_SHALLOW true
SOURCE_SUBDIR cmake_bypass_empty_dir
)

FetchContent_MakeAvailable(cpptrace)
FetchContent_MakeAvailable(plog)
FetchContent_MakeAvailable(bluez_inc)
set_target_properties(Binc PROPERTIES POSITION_INDEPENDENT_CODE ON)

add_library(bt_common SHARED
src/bt_common_c.cpp
src/bt_common_utils.cpp
src/bt_connection.cpp
src/bt_bond_manager.cpp
src/custom_bluez.cpp
)

target_compile_definitions(bt_common PRIVATE BT_COMMON_EXPORTS)

target_include_directories(bt_common
PUBLIC include
PRIVATE ${plog_SOURCE_DIR}/include
PRIVATE ${GLIB_INCLUDE_DIRS}
PRIVATE ${bluez_inc_SOURCE_DIR}
)

target_link_libraries(bt_common
PRIVATE Binc
PRIVATE ${GLIB_LIBRARIES}
PRIVATE $<$<CONFIG:Debug>:cpptrace::cpptrace>
)

set_target_properties(bt_common PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
WINDOWS_EXPORT_ALL_SYMBOLS OFF
)

# -- Testing ------------------------------------------------------
enable_testing()

FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.15.2
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

add_executable(bt_common_tests tests/bt_connection_test.cpp tests/bt_bond_test.cpp)

target_compile_options(bt_common_tests PRIVATE -fsanitize=address -g -O0)
target_link_options(bt_common_tests PRIVATE -fsanitize=address)
target_link_libraries(bt_common_tests
PRIVATE bt_common
PRIVATE GTest::gtest_main
PRIVATE -fsanitize=address
)
set_target_properties(bt_common_tests PROPERTIES
BUILD_RPATH "${CMAKE_BINARY_DIR}/lib"
)
include(GoogleTest)
gtest_discover_tests(bt_common_tests)
64 changes: 64 additions & 0 deletions cpp/linux/bt_common/include/bt_bond_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifndef LINUX_BT_BOND_MANAGER_H
#define LINUX_BT_BOND_MANAGER_H

#include <binc/adapter.h>
#include <future>
#include <gio/gio.h>
#include <glib-object.h>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <utility>

#include "bt_common_defination.h"
#include "bt_enums.h"

struct bluetooth_bond_callback_responder {
std::string device_address;
Device* device_raw = nullptr;
std::string request_type;
GDBusMethodInvocation* dbus_invocation = nullptr;

bluetooth_bond_callback_responder(std::string addr, Device* device, std::string type,
GDBusMethodInvocation* invocation = nullptr)
: device_address(std::move(addr)), device_raw(device), request_type(std::move(type)),
dbus_invocation(invocation) {}
};

class bluetooth_bond_manager : public std::enable_shared_from_this<bluetooth_bond_manager> {
public:
bluetooth_bond_manager();
~bluetooth_bond_manager();

static bt_bond_state get_bond_state(const std::string& device_address) ;

std::future<void> request_and_register_bond_callback(const std::string& device_address,
bt_bond_pairing_callback callback);

void unregister_bond_callback();

// UI Response handlers
void accept_connection(const std::string& pin, const bt_bond_responder_handle& callback_responder) const;
void cancel_connection(const bt_bond_responder_handle& callback_responder) const;

// Internal event triggers from D-Bus handle_method_call
void handle_display_passkey_event(const std::string& device_path, guint32 passkey) const;
bool handle_request_confirmation_event(const std::string& device_path, guint32 passkey,
GDBusMethodInvocation* invocation) const;
void handle_cancel_event() const;

private:
mutable std::mutex m_mutex;
Adapter* m_adapter = nullptr;
Agent* bond_m_agent = nullptr;
guint m_custom_agent_id = 0;

bt_bond_pairing_callback m_pairing_callback{.on_results = nullptr, .on_confirm_pin = nullptr, .on_error = nullptr};
mutable std::set<std::shared_ptr<bluetooth_bond_callback_responder>> m_responders;

GDBusMethodInvocation* m_active_invocation = nullptr;
std::string m_active_device_path;
};

#endif // LINUX_BT_BOND_MANAGER_H
47 changes: 47 additions & 0 deletions cpp/linux/bt_common/include/bt_common_c.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#ifndef LINUX_BT_COMMON_C_H
#define LINUX_BT_COMMON_C_H

#include <stdbool.h>
#include <stdint.h>

#include "bt_common_defination.h"

#if defined(__GNUC__) || defined(__clang__)
#define BT_COMMON_API __attribute__((visibility("default")))
#else
#define BT_COMMON_API
#endif

#ifdef __cplusplus
extern "C" {
#endif

// Global utility functions
BT_COMMON_API void init_logger();
BT_COMMON_API bool ble_is_bluetooth_active();
BT_COMMON_API bool ble_is_secure_connection_available();
BT_COMMON_API bool ble_is_peripheral_role_supported();

// functions to check the pairing capabilities
BT_COMMON_API enum bt_bond_state is_device_bonded(const char* device_address);
BT_COMMON_API enum bt_request_enable_status request_bluetooth_enable();

BT_COMMON_API enum bt_bond_response request_bond(const char* device_address, uint32_t timeout_in_millis);

// callbacks to register and unregister a listener
BT_COMMON_API void bluetooth_caller_register_listener(bt_status_callback callback);
BT_COMMON_API void bluetooth_caller_unregister_listener();

BT_COMMON_API bt_bond_manager_handle create_bond_manager();
BT_COMMON_API void destroy_bond_manager(bt_bond_manager_handle handle);
BT_COMMON_API void bond_manager_request_pairing(bt_bond_manager_handle handle, const char* device_address,
bt_bond_pairing_callback callback);
BT_COMMON_API void bond_manager_unregister_pairing(bt_bond_manager_handle handle);
BT_COMMON_API void bond_manager_accept_connection(bt_bond_manager_handle handle, const char* pin,
bt_bond_responder_handle responder);
BT_COMMON_API void bond_manager_reject_connection(bt_bond_manager_handle handle, bt_bond_responder_handle responder);
#ifdef __cplusplus
}
#endif

#endif // LINUX_BT_COMMON_C_H
27 changes: 27 additions & 0 deletions cpp/linux/bt_common/include/bt_common_defination.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef BT_COMMON_DEFINITION
#define BT_COMMON_DEFINITION

#include "bt_enums.h"
#include <stdbool.h>

// Instance-based caller for bluetooth caller
typedef void* bt_active_caller_ptr;
typedef void (*bt_status_callback)(bool is_on);

// instance handle for the bond_manager class
typedef void* bt_bond_manager_handle;

// instance handle to store the args references for device pairing args
typedef void* bt_bond_responder_handle;

typedef void (*bt_on_bond_error)(enum bt_bond_request_error_code code);
typedef void (*bt_on_bond_results)(enum bt_bond_response resp);
typedef void (*bt_bond_on_req_confirm_pin)(const char* display_pin, void* data);

typedef struct {
bt_on_bond_results on_results;
bt_bond_on_req_confirm_pin on_confirm_pin;
bt_on_bond_error on_error;
} bt_bond_pairing_callback;

#endif
21 changes: 21 additions & 0 deletions cpp/linux/bt_common/include/bt_common_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef LINUX_BT_UTILS_H
#define LINUX_BT_UTILS_H

#include <binc/adapter.h>
#include <cstdint>
#include <glib-object.h>
#include <plog/Log.h>
#include <string>

namespace utils {
void init_logger();
uint64_t parse_mac_address(const std::string& mac_str);
void show_stacktrace();
gboolean check_adapter_role_supported(const Adapter* adapter, const char* role_search);
} // namespace utils

#ifndef LINUX_LOG
#define LINUX_LOG(msg) PLOG_DEBUG << " [" << "LINUX_BT_COMMON" << "] " << msg
#endif

#endif
41 changes: 41 additions & 0 deletions cpp/linux/bt_common/include/bt_connection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef LINUX_BT_CALLS_H
#define LINUX_BT_CALLS_H

#include "bt_enums.h"

#include <binc/adapter.h>

#include <functional>
#include <future>
#include <mutex>

class bt_connection {
public:
static bt_connection& instance();

bt_connection(const bt_connection&) = delete;
bt_connection& operator=(const bt_connection&) = delete;

std::future<bool> is_ble_secure_connection_available();
std::future<bool> is_peripheral_role_supported();
std::future<bool> is_bluetooth_active();

std::future<void> register_bt_listener(const std::function<void(bool)>& callback);
void unregister_bt_listener();

static bt_request_enable_status request_bt_enable();

private:
bt_connection();
~bt_connection();

static void on_adapter_powered_changed(Adapter* adapter, gboolean is_powered);

std::mutex m_mutex;
Adapter* m_adapter = nullptr;

std::function<void(bool)> m_onStatusChange;
bool m_isListenerRegistered = false;
};

#endif
63 changes: 63 additions & 0 deletions cpp/linux/bt_common/include/bt_enums.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef LINUX_BT_COMMON_ENUMS_H
#define LINUX_BT_COMMON_ENUMS_H

#include <cstdint>

#ifdef __cplusplus
extern "C" {
#endif

enum bt_bond_state : int32_t {
DEVICE_BONDED = 1,
DEVICE_NOT_BONDED = 2,
ERROR_INVALID_DEVICE = 3,
ERROR_UNKNOWN = 4,
};

enum bt_bond_response : int32_t {
RESPONSE_PAIRED = 0,
RESPONSE_NOT_READY_TO_PAIR = 1,
RESPONSE_NOT_PAIRED = 2,
RESPONSE_ALREADY_PAIRED = 3,
RESPONSE_CONNECTION_REJECTED = 4,
RESPONSE_TOO_MANY_CONNECTION = 5,
RESPONSE_HARDWARE_FAILURE = 6,
RESPONSE_AUTHENTICATION_TIMEOUT = 7,
RESPONSE_AUTHENTICATION_NOT_ALLOWED = 8,
RESPONSE_AUTHENTICATION_FAILURE = 9,
RESPONSE_NO_SUPPORTED_PROFILES = 10,
RESPONSE_PROTECTION_LEVEL_ISSUES = 11,
RESPONSE_ACCESS_DENIED = 12,
RESPONSE_INVALID_DATA = 13,
RESPONSE_PARING_OPERATION_CANCELLED = 14,
RESPONSE_OPERATION_IN_PROGRESS = 15,
RESPONSE_HANDLER_NOT_REGISTERED = 16,
RESPONSE_REJECTED_BY_HANDLER = 17,
RESPONSE_REMOVE_DEVICE_IS_ASSOCIATED = 18,
RESPONSE_FAILED = 19,

};

enum bt_bond_request_error_code : int32_t {
BT_BOND_REQUEST_ERROR_ALREADY_BONDED = 1,
BT_BOND_REQUEST_ERROR_CANNOT_BE_BONDED = 2,
BT_BOND_REQUEST_ERROR_DEVICE_CANNOT_BE_FOUND = 3,
BT_BOND_REQUEST_ERROR_UNKNOWN = 4,
BT_BOND_REQUEST_ERROR_OPERATION_CANCELLED = 5,
};

enum bt_request_enable_status : int32_t {
REQUEST_ACCEPTED = 0,
REQUEST_DENIED_PRIVACY_ISSUES = 1,
REQUEST_DENIED_BY_SYSTEM = 2,
REQUEST_DENIED_BY_USER = 3,
REQUEST_DENIED_CANNOT_FIND_ADAPTER = 4,
REQUEST_DENIED_UNKNOWN = 5,
REQUEST_NOT_NEEDED = 6,
};

#ifdef __cplusplus
}
#endif

#endif
Loading