[libamb] - added value quality, removed deprecated GetFoo call, made updateFrequency...
[profile/ivi/automotive-message-broker.git] / plugins / common / bluetooth5.cpp
1 #include "bluetooth5.h"
2 #include "superptr.hpp"
3
4 #include "debugout.h"
5 #include <gio/gio.h>
6 #include <gio/gunixfdlist.h>
7 #include <string>
8
9 static const gchar introspection_xml[] =
10   "<node>"
11   "  <interface name='org.bluez.Profile1'>"
12   "    <method name='Release'>"
13   "    </method>"
14   "    <method name='NewConnection'>"
15   "      <arg type='o' name='device' direction='in'/>"
16   "      <arg type='h' name='fd' direction='in'/>"
17   "      <arg type='a{sv}' name='fd_properties' direction='in'/>"
18   "    </method>"
19   "    <method name='RequestDisconnection'>"
20   "      <arg type='o' name='device' direction='in'/>"
21   "    </method>"
22   "  </interface>"
23   "</node>";
24
25 static void handleMethodCall(GDBusConnection       *connection,
26                                                          const gchar           *sender,
27                                                          const gchar           *object_path,
28                                                          const gchar           *interface_name,
29                                                          const gchar           *method_name,
30                                                          GVariant              *parameters,
31                                                          GDBusMethodInvocation *invocation,
32                                                          gpointer               user_data)
33 {
34
35         Bluetooth5* manager = static_cast<Bluetooth5*>(user_data);
36
37         std::string method = method_name;
38
39         if(method == "Release")
40         {
41
42         }
43         else if(method == "NewConnection")
44         {
45                 DebugOut()<<"NewConnection() called"<<endl;
46
47                 gchar* device;
48                 gint32 fd;
49                 GVariantIter* iter;
50
51                 DebugOut() << "parameters signature: " << g_variant_get_type_string(parameters) << endl;
52
53                 g_variant_get(parameters,"(oha{sv})", &device, &fd, &iter);
54
55                 DebugOut() << "device: " << device << endl;
56
57                 auto message = g_dbus_method_invocation_get_message(invocation);
58
59                 auto fdList = g_dbus_message_get_unix_fd_list(message);
60
61                 GError* error = nullptr;
62
63                 fd = g_unix_fd_list_get(fdList, 0, &error);
64
65                 auto errorPtr = amb::make_super(error);
66
67                 if(errorPtr)
68                 {
69                         DebugOut(DebugOut::Error) << "Error trying to get fd: " << errorPtr->message << endl;
70                         return;
71                 }
72
73                 char* propertyName;
74                 GVariant* value;
75
76                 DebugOut() << "trying to see what properties we got with this call" << endl;
77
78                 while(g_variant_iter_next(iter,"{sv}", &propertyName, &value))
79                 {
80                         auto keyPtr = amb::make_super(propertyName);
81                         auto valuePtr = amb::make_super(value);
82                         DebugOut() << "key " << keyPtr.get() << "value signature: " << g_variant_get_type_string(valuePtr.get()) << endl;
83                 }
84
85                 manager->connected_(fd);
86         }
87         else if(method == "RequestDisconnection")
88         {
89                 DebugOut()<<"disconnection."<<endl;
90         }
91         else
92         {
93                 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
94         }
95
96         /// return nothing:
97         g_dbus_method_invocation_return_value(invocation, nullptr);
98 }
99
100 static GVariant* getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, const gchar* propertyName, GError** error, gpointer userData)
101 {
102         return NULL;
103 }
104
105 static gboolean setProperty(GDBusConnection * connection, const gchar * sender, const gchar *objectPath,
106                                                         const gchar *interfaceName, const gchar * propertyName, GVariant *value,
107                                                         GError** error, gpointer userData)
108 {
109         return false;
110 }
111
112 static const GDBusInterfaceVTable interfaceVTable =
113 {
114         handleMethodCall,
115         getProperty,
116         setProperty
117 };
118
119 std::string findDevice(std::string address, std::string adapterPath)
120 {
121         std::string objectPath;
122
123         GError * proxyError = nullptr;
124         auto managerProxy = amb::make_super(g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL,
125                                                                                                                           "org.bluez",
126                                                                                                                           "/",
127                                                                                                                           "org.freedesktop.DBus.ObjectManager",
128                                                                                                                           nullptr, &proxyError));
129
130         auto proxyErrorPtr = amb::make_super(proxyError);
131         if(proxyErrorPtr)
132         {
133                 DebugOut(DebugOut::Error)<<"Could not create ObjectManager proxy for Bluez: "<<proxyErrorPtr->message<<endl;
134                 return "";
135         }
136
137         GError * getManagerObjectError = nullptr;
138
139         auto objectMap = amb::make_super(g_dbus_proxy_call_sync(managerProxy.get(), "GetManagedObjects",nullptr, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &getManagerObjectError));
140
141         auto getManagerObjectErrorPtr = amb::make_super(getManagerObjectError);
142
143         if(getManagerObjectErrorPtr)
144         {
145                 DebugOut(DebugOut::Error)<<"Failed call to GetManagedObjects: "<<getManagerObjectErrorPtr->message<<endl;
146                 return "";
147         }
148
149         GVariantIter* iter;
150         char* objPath;
151         GVariantIter* level2Dict;
152
153         g_variant_get(objectMap.get(), "(a{oa{sa{sv}}})",&iter);
154
155         auto iterPtr = amb::make_super(iter);
156
157
158         while(g_variant_iter_next(iter, "{oa{sa{sv}}}",&objPath, &level2Dict))
159         {
160                 auto level2DictPtr = amb::make_super(level2Dict);
161                 auto objPathPtr = amb::make_super(objPath);
162
163                 char * interfaceName;
164                 GVariantIter* innerDict;
165                 while(g_variant_iter_next(level2DictPtr.get(), "{sa{sv}}", &interfaceName, &innerDict))
166                 {
167                         auto interfaceNamePtr = amb::make_super(interfaceName);
168                         auto innerDictPtr = amb::make_super(innerDict);
169                         if(std::string(interfaceNamePtr.get()) == "org.bluez.Device1")
170                         {
171                                 char* propertyName;
172                                 GVariant* value;
173
174                                 while(objectPath == "" && g_variant_iter_next(innerDictPtr.get(),"{sv}", &propertyName, &value))
175                                 {
176                                         auto propertyNamePtr = amb::make_super(propertyName);
177                                         auto valuePtr = amb::make_super(value);
178
179                                         if(std::string(propertyNamePtr.get()) == "Address")
180                                         {
181                                                 char* addy;
182                                                 g_variant_get(valuePtr.get(),"s",&addy);
183
184                                                 auto addyPtr = amb::make_super(addy);
185
186                                                 if(addyPtr && std::string(addyPtr.get()) == address)
187                                                 {
188                                                         objectPath = objPathPtr.get();
189                                                 }
190                                         }
191                                         ///TODO: filter only devices that have the specified adapter
192                                 }
193                         }
194                 }
195         }
196
197         return objectPath;
198 }
199
200 Bluetooth5::Bluetooth5()
201 {
202         GError* errorIntrospection = NULL;
203
204         GDBusNodeInfo* introspection = g_dbus_node_info_new_for_xml(introspection_xml, &errorIntrospection);
205
206         auto errorIntrospectionPtr = amb::make_super(errorIntrospection);
207
208         if(errorIntrospectionPtr)
209         {
210                 DebugOut(DebugOut::Error)<<"in instrospection xml: "<<errorIntrospectionPtr->message<<endl;
211                 return;
212         }
213
214         GError* errorBus = nullptr;
215
216         GDBusInterfaceInfo* mInterfaceInfo = g_dbus_node_info_lookup_interface(introspection, "org.bluez.Profile1");
217
218         mConnection = amb::make_super(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &errorBus));
219
220         auto errorBusPtr = amb::make_super(errorBus);
221
222         if(errorBusPtr)
223         {
224                 DebugOut(DebugOut::Error)<<"getting system bus: "<<errorBusPtr->message<<endl;
225                 return;
226         }
227
228         GError* errorRegister = nullptr;
229
230         int regId = g_dbus_connection_register_object(mConnection.get(), "/org/bluez/spp", mInterfaceInfo, &interfaceVTable, this, NULL, &errorRegister);
231
232         auto errorRegisterPtr = amb::make_super(errorRegister);
233
234         if(errorRegisterPtr)
235         {
236
237                 DebugOut(DebugOut::Error)<<"Registering org.bluez.Profile1 Interface: "<<errorRegisterPtr->message<<endl;
238                 return;
239         }
240
241         GVariantBuilder builder;
242         g_variant_builder_init(&builder, G_VARIANT_TYPE_DICTIONARY);
243
244         g_variant_builder_add(&builder, "{sv}", "Name", g_variant_new("s","AMB spp client"));
245         g_variant_builder_add(&builder, "{sv}", "Role", g_variant_new("s","client"));
246         g_variant_builder_add(&builder, "{sv}", "AutoConnect", g_variant_new("b",true));
247
248         GError* errorRegisterCall = nullptr;
249
250         g_dbus_connection_call_sync(mConnection.get(),
251                                                                 "org.bluez",
252                                                                 "/org/bluez",
253                                                                 "org.bluez.ProfileManager1",
254                                                                 "RegisterProfile",
255                                                                 g_variant_new("(osa{sv})", "/org/bluez/spp", "00001101-0000-1000-8000-00805F9B34FB", &builder),
256                                                                 nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &errorRegisterCall);
257
258         auto errorRegisterCallPtr = amb::make_super(errorRegisterCall);
259
260         if(errorRegisterCallPtr)
261         {
262                 DebugOut(DebugOut::Error)<<"RegisterProfile failed: "<<errorRegisterCallPtr->message<<endl;
263                 return;
264         }
265 }
266
267 bool Bluetooth5::setDevice(string address)
268 {
269         mPath = findDevice(address);
270
271
272         if(mPath == "")
273         {
274                 DebugOut(DebugOut::Error) << "device path not found.  Not paired? " << endl;
275                 return false;
276         }
277
278         return true;
279 }
280
281 void Bluetooth5::getDeviceForAddress(std::string address, ConnectedCallback connectedCallback)
282 {
283         mConnected = connectedCallback;
284
285         if(!setDevice(address))
286                 return;
287
288         DebugOut() << "Bluetooth device path: " << mPath << endl;
289
290         connect(connectedCallback);
291 }
292
293 void Bluetooth5::connected_(int fd)
294 {
295         try
296         {
297                 mConnected(fd);
298         }
299         catch(...)
300         {
301                 DebugOut(DebugOut::Error) << "Error calling connected callback" << endl;
302         }
303 }
304
305 void Bluetooth5::connect(ConnectedCallback onconnectedCallback)
306 {
307         mConnected = onconnectedCallback;
308
309         GError* error = nullptr;
310
311         auto  deviceProxyPtr = amb::make_super(g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,G_DBUS_PROXY_FLAGS_NONE,NULL,
312                                                                                                                           "org.bluez", mPath.c_str(), "org.bluez.Device1", nullptr, &error));
313
314         auto errorPtr = amb::make_super(error);
315
316         if(errorPtr)
317         {
318                 DebugOut(DebugOut::Error) << "Error getting bluetooth device proxy " << errorPtr->message <<endl;
319                 return;
320         }
321
322         g_dbus_proxy_call(deviceProxyPtr.get(), "Connect", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr,
323                                           [](GObject *source_object, GAsyncResult *res, gpointer user_data)
324         {
325
326                 GError* error = nullptr;
327
328                 g_dbus_proxy_call_finish(G_DBUS_PROXY (source_object), res, &error);
329
330                 auto errorPtr = amb::make_super(error);
331
332                 if(errorPtr)
333                 {
334                         DebugOut(DebugOut::Warning) << "error trying to connect profile: " << errorPtr->message << endl;
335                 }
336         },
337         this);
338 }
339
340 void Bluetooth5::disconnect()
341 {
342         GError* error = nullptr;
343
344         auto  deviceProxyPtr = amb::make_super(g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,G_DBUS_PROXY_FLAGS_NONE,NULL,
345                                                                                                                           "org.bluez", mPath.c_str(), "org.bluez.Device1", nullptr, &error));
346
347         auto errorPtr = amb::make_super(error);
348
349         if(errorPtr)
350         {
351                 DebugOut(DebugOut::Error) << "Error getting bluetooth device proxy " << errorPtr->message <<endl;
352                 return;
353         }
354
355         g_dbus_proxy_call(deviceProxyPtr.get(), "Disconnect", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr,[](GObject *source_object,
356                                           GAsyncResult *res, gpointer user_data){
357                 GError* error = nullptr;
358
359                 g_dbus_proxy_call_finish(G_DBUS_PROXY (source_object), res, &error);
360
361                 auto errorPtr = amb::make_super(error);
362
363                 if(errorPtr)
364                 {
365                         DebugOut(DebugOut::Error) << "error trying to disconnect: " << errorPtr->message << endl;
366                 }
367         }, nullptr);
368 }