summaryrefslogtreecommitdiff
path: root/plugins/common/bluetooth5.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/common/bluetooth5.cpp')
-rw-r--r--plugins/common/bluetooth5.cpp368
1 files changed, 368 insertions, 0 deletions
diff --git a/plugins/common/bluetooth5.cpp b/plugins/common/bluetooth5.cpp
new file mode 100644
index 00000000..c2c63275
--- /dev/null
+++ b/plugins/common/bluetooth5.cpp
@@ -0,0 +1,368 @@
+#include "bluetooth5.h"
+#include "superptr.hpp"
+
+#include "debugout.h"
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <string>
+
+static const gchar introspection_xml[] =
+ "<node>"
+ " <interface name='org.bluez.Profile1'>"
+ " <method name='Release'>"
+ " </method>"
+ " <method name='NewConnection'>"
+ " <arg type='o' name='device' direction='in'/>"
+ " <arg type='h' name='fd' direction='in'/>"
+ " <arg type='a{sv}' name='fd_properties' direction='in'/>"
+ " </method>"
+ " <method name='RequestDisconnection'>"
+ " <arg type='o' name='device' direction='in'/>"
+ " </method>"
+ " </interface>"
+ "</node>";
+
+static void handleMethodCall(GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+
+ Bluetooth5* manager = static_cast<Bluetooth5*>(user_data);
+
+ std::string method = method_name;
+
+ if(method == "Release")
+ {
+
+ }
+ else if(method == "NewConnection")
+ {
+ DebugOut()<<"NewConnection() called"<<endl;
+
+ gchar* device;
+ gint32 fd;
+ GVariantIter* iter;
+
+ DebugOut() << "parameters signature: " << g_variant_get_type_string(parameters) << endl;
+
+ g_variant_get(parameters,"(oha{sv})", &device, &fd, &iter);
+
+ DebugOut() << "device: " << device << endl;
+
+ auto message = g_dbus_method_invocation_get_message(invocation);
+
+ auto fdList = g_dbus_message_get_unix_fd_list(message);
+
+ GError* error = nullptr;
+
+ fd = g_unix_fd_list_get(fdList, 0, &error);
+
+ auto errorPtr = amb::make_super(error);
+
+ if(errorPtr)
+ {
+ DebugOut(DebugOut::Error) << "Error trying to get fd: " << errorPtr->message << endl;
+ return;
+ }
+
+ char* propertyName;
+ GVariant* value;
+
+ DebugOut() << "trying to see what properties we got with this call" << endl;
+
+ while(g_variant_iter_next(iter,"{sv}", &propertyName, &value))
+ {
+ auto keyPtr = amb::make_super(propertyName);
+ auto valuePtr = amb::make_super(value);
+ DebugOut() << "key " << keyPtr.get() << "value signature: " << g_variant_get_type_string(valuePtr.get()) << endl;
+ }
+
+ manager->connected_(fd);
+ }
+ else if(method == "RequestDisconnection")
+ {
+ DebugOut()<<"disconnection."<<endl;
+ }
+ else
+ {
+ g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
+ }
+
+ /// return nothing:
+ g_dbus_method_invocation_return_value(invocation, nullptr);
+}
+
+static GVariant* getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, const gchar* propertyName, GError** error, gpointer userData)
+{
+ return NULL;
+}
+
+static gboolean setProperty(GDBusConnection * connection, const gchar * sender, const gchar *objectPath,
+ const gchar *interfaceName, const gchar * propertyName, GVariant *value,
+ GError** error, gpointer userData)
+{
+ return false;
+}
+
+static const GDBusInterfaceVTable interfaceVTable =
+{
+ handleMethodCall,
+ getProperty,
+ setProperty
+};
+
+std::string findDevice(std::string address, std::string adapterPath)
+{
+ std::string objectPath;
+
+ GError * proxyError = nullptr;
+ auto managerProxy = amb::make_super(g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL,
+ "org.bluez",
+ "/",
+ "org.freedesktop.DBus.ObjectManager",
+ nullptr, &proxyError));
+
+ auto proxyErrorPtr = amb::make_super(proxyError);
+ if(proxyErrorPtr)
+ {
+ DebugOut(DebugOut::Error)<<"Could not create ObjectManager proxy for Bluez: "<<proxyErrorPtr->message<<endl;
+ return "";
+ }
+
+ GError * getManagerObjectError = nullptr;
+
+ auto objectMap = amb::make_super(g_dbus_proxy_call_sync(managerProxy.get(), "GetManagedObjects",nullptr, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &getManagerObjectError));
+
+ auto getManagerObjectErrorPtr = amb::make_super(getManagerObjectError);
+
+ if(getManagerObjectErrorPtr)
+ {
+ DebugOut(DebugOut::Error)<<"Failed call to GetManagedObjects: "<<getManagerObjectErrorPtr->message<<endl;
+ return "";
+ }
+
+ GVariantIter* iter;
+ char* objPath;
+ GVariantIter* level2Dict;
+
+ g_variant_get(objectMap.get(), "(a{oa{sa{sv}}})",&iter);
+
+ auto iterPtr = amb::make_super(iter);
+
+
+ while(g_variant_iter_next(iter, "{oa{sa{sv}}}",&objPath, &level2Dict))
+ {
+ auto level2DictPtr = amb::make_super(level2Dict);
+ auto objPathPtr = amb::make_super(objPath);
+
+ char * interfaceName;
+ GVariantIter* innerDict;
+ while(g_variant_iter_next(level2DictPtr.get(), "{sa{sv}}", &interfaceName, &innerDict))
+ {
+ auto interfaceNamePtr = amb::make_super(interfaceName);
+ auto innerDictPtr = amb::make_super(innerDict);
+ if(std::string(interfaceNamePtr.get()) == "org.bluez.Device1")
+ {
+ char* propertyName;
+ GVariant* value;
+
+ while(objectPath == "" && g_variant_iter_next(innerDictPtr.get(),"{sv}", &propertyName, &value))
+ {
+ auto propertyNamePtr = amb::make_super(propertyName);
+ auto valuePtr = amb::make_super(value);
+
+ if(std::string(propertyNamePtr.get()) == "Address")
+ {
+ char* addy;
+ g_variant_get(valuePtr.get(),"s",&addy);
+
+ auto addyPtr = amb::make_super(addy);
+
+ if(addyPtr && std::string(addyPtr.get()) == address)
+ {
+ objectPath = objPathPtr.get();
+ }
+ }
+ ///TODO: filter only devices that have the specified adapter
+ }
+ }
+ }
+ }
+
+ return objectPath;
+}
+
+Bluetooth5::Bluetooth5()
+{
+ GError* errorIntrospection = NULL;
+
+ GDBusNodeInfo* introspection = g_dbus_node_info_new_for_xml(introspection_xml, &errorIntrospection);
+
+ auto errorIntrospectionPtr = amb::make_super(errorIntrospection);
+
+ if(errorIntrospectionPtr)
+ {
+ DebugOut(DebugOut::Error)<<"in instrospection xml: "<<errorIntrospectionPtr->message<<endl;
+ return;
+ }
+
+ GError* errorBus = nullptr;
+
+ GDBusInterfaceInfo* mInterfaceInfo = g_dbus_node_info_lookup_interface(introspection, "org.bluez.Profile1");
+
+ mConnection = amb::make_super(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &errorBus));
+
+ auto errorBusPtr = amb::make_super(errorBus);
+
+ if(errorBusPtr)
+ {
+ DebugOut(DebugOut::Error)<<"getting system bus: "<<errorBusPtr->message<<endl;
+ return;
+ }
+
+ GError* errorRegister = nullptr;
+
+ int regId = g_dbus_connection_register_object(mConnection.get(), "/org/bluez/spp", mInterfaceInfo, &interfaceVTable, this, NULL, &errorRegister);
+
+ auto errorRegisterPtr = amb::make_super(errorRegister);
+
+ if(errorRegisterPtr)
+ {
+
+ DebugOut(DebugOut::Error)<<"Registering org.bluez.Profile1 Interface: "<<errorRegisterPtr->message<<endl;
+ return;
+ }
+
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_DICTIONARY);
+
+ g_variant_builder_add(&builder, "{sv}", "Name", g_variant_new("s","AMB spp client"));
+ g_variant_builder_add(&builder, "{sv}", "Role", g_variant_new("s","client"));
+ g_variant_builder_add(&builder, "{sv}", "AutoConnect", g_variant_new("b",true));
+
+ GError* errorRegisterCall = nullptr;
+
+ g_dbus_connection_call_sync(mConnection.get(),
+ "org.bluez",
+ "/org/bluez",
+ "org.bluez.ProfileManager1",
+ "RegisterProfile",
+ g_variant_new("(osa{sv})", "/org/bluez/spp", "00001101-0000-1000-8000-00805F9B34FB", &builder),
+ nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &errorRegisterCall);
+
+ auto errorRegisterCallPtr = amb::make_super(errorRegisterCall);
+
+ if(errorRegisterCallPtr)
+ {
+ DebugOut(DebugOut::Error)<<"RegisterProfile failed: "<<errorRegisterCallPtr->message<<endl;
+ return;
+ }
+}
+
+bool Bluetooth5::setDevice(string address)
+{
+ mPath = findDevice(address);
+
+
+ if(mPath == "")
+ {
+ DebugOut(DebugOut::Error) << "device path not found. Not paired? " << endl;
+ return false;
+ }
+
+ return true;
+}
+
+void Bluetooth5::getDeviceForAddress(std::string address, ConnectedCallback connectedCallback)
+{
+ mConnected = connectedCallback;
+
+ if(!setDevice(address))
+ return;
+
+ DebugOut() << "Bluetooth device path: " << mPath << endl;
+
+ connect(connectedCallback);
+}
+
+void Bluetooth5::connected_(int fd)
+{
+ try
+ {
+ mConnected(fd);
+ }
+ catch(...)
+ {
+ DebugOut(DebugOut::Error) << "Error calling connected callback" << endl;
+ }
+}
+
+void Bluetooth5::connect(ConnectedCallback onconnectedCallback)
+{
+ mConnected = onconnectedCallback;
+
+ GError* error = nullptr;
+
+ auto deviceProxyPtr = amb::make_super(g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,G_DBUS_PROXY_FLAGS_NONE,NULL,
+ "org.bluez", mPath.c_str(), "org.bluez.Device1", nullptr, &error));
+
+ auto errorPtr = amb::make_super(error);
+
+ if(errorPtr)
+ {
+ DebugOut(DebugOut::Error) << "Error getting bluetooth device proxy " << errorPtr->message <<endl;
+ return;
+ }
+
+ g_dbus_proxy_call(deviceProxyPtr.get(), "Connect", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr,
+ [](GObject *source_object, GAsyncResult *res, gpointer user_data)
+ {
+
+ GError* error = nullptr;
+
+ g_dbus_proxy_call_finish(G_DBUS_PROXY (source_object), res, &error);
+
+ auto errorPtr = amb::make_super(error);
+
+ if(errorPtr)
+ {
+ DebugOut(DebugOut::Warning) << "error trying to connect profile: " << errorPtr->message << endl;
+ }
+ },
+ this);
+}
+
+void Bluetooth5::disconnect()
+{
+ GError* error = nullptr;
+
+ auto deviceProxyPtr = amb::make_super(g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,G_DBUS_PROXY_FLAGS_NONE,NULL,
+ "org.bluez", mPath.c_str(), "org.bluez.Device1", nullptr, &error));
+
+ auto errorPtr = amb::make_super(error);
+
+ if(errorPtr)
+ {
+ DebugOut(DebugOut::Error) << "Error getting bluetooth device proxy " << errorPtr->message <<endl;
+ return;
+ }
+
+ g_dbus_proxy_call(deviceProxyPtr.get(), "Disconnect", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr,[](GObject *source_object,
+ GAsyncResult *res, gpointer user_data){
+ GError* error = nullptr;
+
+ g_dbus_proxy_call_finish(G_DBUS_PROXY (source_object), res, &error);
+
+ auto errorPtr = amb::make_super(error);
+
+ if(errorPtr)
+ {
+ DebugOut(DebugOut::Error) << "error trying to disconnect: " << errorPtr->message << endl;
+ }
+ }, nullptr);
+}