Initial checkin for phone daemon for the HTML5 UI
[profile/ivi/phoned.git] / src / bluez.cpp
1 #include "bluez.h"
2 #include "utils.h"
3
4 #include <gio/gio.h>
5 #include <stdio.h>
6 #include <string.h>
7
8 #include <stdlib.h>
9 #include <dbus/dbus.h>
10
11 #include "Logger.h"
12
13 namespace PhoneD {
14
15 #define BLUEZ_PREFIX            "org.bluez"
16
17 #define BLUEZ_SERVICE           BLUEZ_PREFIX
18 #define BLUEZ_MANAGER_IFACE     BLUEZ_PREFIX ".Manager"
19 #define BLUEZ_ADAPTER_IFACE     BLUEZ_PREFIX ".Adapter"
20 #define BLUEZ_DEVICE_IFACE      BLUEZ_PREFIX ".Device"
21 #define BLUEZ_AGENT_IFACE       BLUEZ_PREFIX ".Agent"
22
23 #define AGENT_PATH              "/org/bluez/agent_poc"
24 #define AGENT_CAPABILITIES      "KeyboardDisplay"
25
26 #define AGENT_PASSKEY            123456
27 #define AGENT_PINCODE           "123456"
28
29 #define AGENT_INTERFACE_XML                                 \
30     "<node>"                                                \
31     "  <interface name='org.bluez.Agent'>"                  \
32     "    <method name='Release'>"                           \
33     "    </method>"                                         \
34     "    <method name='Authorize'>"                         \
35     "      <arg type='o' name='device' direction='in'/>"    \
36     "      <arg type='s' name='uuid' direction='in'/>"      \
37     "    </method>"                                         \
38     "    <method name='RequestPinCode'>"                    \
39     "      <arg type='o' name='device' direction='in'/>"    \
40     "      <arg type='s' name='pincode' direction='out'/>"  \
41     "    </method>"                                         \
42     "    <method name='RequestPasskey'>"                    \
43     "      <arg type='o' name='device' direction='in'/>"    \
44     "      <arg type='u' name='passkey' direction='out'/>"  \
45     "    </method>"                                         \
46     "    <method name='DisplayPasskey'>"                    \
47     "      <arg type='o' name='device' direction='in'/>"    \
48     "      <arg type='u' name='passkey' direction='in'/>"   \
49     "    </method>"                                         \
50     "    <method name='DisplayPinCode'>"                    \
51     "      <arg type='o' name='device' direction='in'/>"    \
52     "      <arg type='s' name='pincode' direction='in'/>"   \
53     "    </method>"                                         \
54     "    <method name='RequestConfirmation'>"               \
55     "      <arg type='o' name='device' direction='in'/>"    \
56     "      <arg type='u' name='passkey' direction='in'/>"   \
57     "    </method>"                                         \
58     "    <method name='ConfirmModeChange'>"                 \
59     "      <arg type='s' name='mode' direction='in'/>"      \
60     "    </method>"                                         \
61     "    <method name='Cancel'>"                            \
62     "    </method>"                                         \
63     "  </interface>"                                        \
64     "</node>"
65
66 /* NOTE:
67  * "Release"             ... does nothing
68  * "Authorize"           ... automatically authorized
69  * "RequestPinCode"      ... used default pin code (AGENT_PINCODE)
70  * "RequestPasskey"      ... used default passkey (AGENT_PASSKEY)
71  * "RequestConfirmation" ... automatically confirmed
72  * "DisplayPinCode"      ... does nothing
73  * "DisplayPasskey"      ... does nothing
74  * "ConfirmModeChange"   ... automatically confirmed
75  * "Cancel"              ... does nothing
76  */
77
78 Bluez::Bluez() :
79     mAdapterPath(NULL),
80     mAgentRegistrationId(-1),
81     mAgentIntrospectionData(NULL)
82 {
83     LoggerD("entered");
84
85     mAdapterPath = getDefaultAdapter();
86     if(!mAdapterPath) {
87         LoggerE("Unable to get default adapter");
88     }
89     memset(&mAgentIfaceVTable, 0, sizeof(mAgentIfaceVTable));
90
91     // subscribe for AdapterAdded/AdapterRemoved to get notification about the change
92     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, BLUEZ_SERVICE, BLUEZ_MANAGER_IFACE,
93                              "/", "AdapterAdded", Bluez::handleSignal,
94                              this);
95     Utils::setSignalListener(G_BUS_TYPE_SYSTEM, BLUEZ_SERVICE, BLUEZ_MANAGER_IFACE,
96                              "/", "AdapterRemoved", Bluez::handleSignal,
97                              this);
98
99     if(mAdapterPath) {
100         Utils::setSignalListener(G_BUS_TYPE_SYSTEM, BLUEZ_SERVICE, BLUEZ_ADAPTER_IFACE,
101                                  mAdapterPath, "DeviceCreated", Bluez::handleSignal,
102                                  this);
103         Utils::setSignalListener(G_BUS_TYPE_SYSTEM, BLUEZ_SERVICE, BLUEZ_ADAPTER_IFACE,
104                                  mAdapterPath, "DeviceRemoved", Bluez::handleSignal,
105                                  this);
106         Utils::setSignalListener(G_BUS_TYPE_SYSTEM, BLUEZ_SERVICE, BLUEZ_ADAPTER_IFACE,
107                                  mAdapterPath, "PropertyChanged", Bluez::handleSignal,
108                                  this);
109     }
110 }
111
112 Bluez::~Bluez() {
113     if(mAdapterPath) {
114         free(mAdapterPath);
115         mAdapterPath = NULL;
116     }
117 }
118
119 gchar* Bluez::getDefaultAdapter()
120 {
121     GError *err = NULL;
122     GVariant *reply = NULL;
123     reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
124                                          BLUEZ_SERVICE,
125                                          "/",
126                                          BLUEZ_MANAGER_IFACE,
127                                          "DefaultAdapter",
128                                          NULL,
129                                          NULL,
130                                          G_DBUS_CALL_FLAGS_NONE,
131                                          -1,
132                                          NULL,
133                                          &err);
134     if(err || !reply) {
135         if(err) {
136             LoggerE("Failed to get default adapter: " << err->message);
137             g_error_free(err);
138         }
139         if(!reply)
140             LoggerE("Reply from 'DefaultAdapter' is null");
141         return NULL;
142     }
143
144     char *adapter = NULL;
145     g_variant_get(reply, "(o)", &adapter);
146     LoggerD("DefaultAdapter: " << adapter);
147
148     // make a copy of adapter, 'cause it will be destroyed when 'reply' is un-refed
149     char *result = adapter?strdup(adapter):NULL;
150
151     g_variant_unref(reply);
152
153     return result;
154 }
155
156 bool Bluez::setAdapterPowered(bool value) {
157     if(mAdapterPath) {
158         GError *err = NULL;
159         g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL,NULL),
160                                      BLUEZ_SERVICE,
161                                      mAdapterPath,
162                                      BLUEZ_ADAPTER_IFACE,
163                                      "SetProperty",
164                                      g_variant_new ("(sv)", // floating parameters are consumed, no cleanup/unref needed
165                                          "Powered",
166                                          g_variant_new("b", &value) // floating parameters are consumed, no cleanup/unref needed
167                                      ),
168                                      NULL,
169                                      G_DBUS_CALL_FLAGS_NONE,
170                                      -1,
171                                      NULL,
172                                      &err);
173
174         if(err) {
175             LoggerE("Failed to call \"SetProperty\" DBUS method: " << err->message);
176             g_error_free(err);
177             return false;
178         }
179
180         return true;
181     }
182
183     return false;
184 }
185
186 void Bluez::handleSignal(GDBusConnection  *connection,
187                          const gchar      *sender,
188                          const gchar      *object_path,
189                          const gchar      *interface_name,
190                          const gchar      *signal_name,
191                          GVariant         *parameters,
192                          gpointer          user_data)
193 {
194     LoggerD("signal received: '" << interface_name << "' -> '" << signal_name << "' -> '" << object_path << "'");
195
196     Bluez *ctx = static_cast<Bluez*>(user_data);
197     if(!ctx) {
198         LoggerD("Failed to cast to Bluez");
199         return;
200     }
201
202     if(!strcmp(interface_name, BLUEZ_MANAGER_IFACE)) {
203         if(!strcmp(signal_name, "AdapterAdded")) {
204             const char *adapter = NULL;
205             g_variant_get(parameters, "(o)", &adapter);
206             if(adapter) {
207                 LoggerD("Adapter added: " << adapter);
208                 if(!ctx->mAdapterPath) {
209                     // make added adapter as default
210                     ctx->mAdapterPath = strdup(adapter);
211                     //ctx->setupAgent();
212                     ctx->registerAgent();
213                     ctx->defaultAdapterAdded();
214                 }
215             }
216         }
217         else if(!strcmp(signal_name, "AdapterRemoved")) {
218             const char *adapter = NULL;
219             g_variant_get(parameters, "(o)", &adapter);
220             if(adapter) {
221                 LoggerD("Adapter removed: " << adapter);
222                 if(ctx->mAdapterPath && !strcmp(ctx->mAdapterPath, adapter)) {
223                     // removed the default adapter
224                     free(ctx->mAdapterPath);
225                     ctx->mAdapterPath = NULL;
226                     ctx->defaultAdapterRemoved();
227                 }
228             }
229         }
230     }
231     else if(!strcmp(interface_name, BLUEZ_ADAPTER_IFACE)) {
232         if(!strcmp(signal_name, "DeviceCreated")) {
233             const char *device;
234             g_variant_get(parameters, "(o)", &device);
235             LoggerD("DeviceCreated: " << (device?device:"UNKNOWN"));
236
237             // subscribe for PropertyChanged signal on the device,
238             // to get notification about device being paired
239             Utils::setSignalListener(G_BUS_TYPE_SYSTEM, BLUEZ_SERVICE, BLUEZ_DEVICE_IFACE,
240                                      device, "PropertyChanged", Bluez::handleSignal,
241                                      ctx);
242
243         }
244         else if(!strcmp(signal_name, "DeviceRemoved")) {
245             const char *device;
246             g_variant_get(parameters, "(o)", &device);
247             LoggerD("DeviceRemoved: " << (device?device:"UNKNOWN"));
248             ctx->deviceRemoved(device);
249         }
250         else if(!strcmp(signal_name, "PropertyChanged")) {
251             const char *name;
252             GVariant *v_value;
253             g_variant_get(parameters, "(sv)", &name, &v_value);
254             LoggerD("\tname=" << name);
255             if(!strcmp(name, "Powered")) {
256                 bool value = g_variant_get_boolean(v_value);
257                 ctx->adapterPowered(value);
258                 //LoggerD("\tvalue=" << (value?"TRUE":"FALSE"));
259             }
260         }
261     }
262     else if(!strcmp(interface_name, BLUEZ_DEVICE_IFACE)) {
263         if(!strcmp(signal_name, "PropertyChanged")) {
264             const char *name;
265             GVariant *value;
266             g_variant_get(parameters, "(sv)", &name, &value);
267             if(!strcmp(name, "Paired")) {
268                 bool paired = g_variant_get_boolean(value);
269                 if(paired) { // the device has been paired
270                     ctx->deviceCreated(object_path);
271                 }
272             }
273         }
274     }
275 }
276
277 void Bluez::agentHandleMethodCall( GDBusConnection       *connection,
278                                    const gchar           *sender,
279                                    const gchar           *object_path,
280                                    const gchar           *interface_name,
281                                    const gchar           *method_name,
282                                    GVariant              *parameters,
283                                    GDBusMethodInvocation *invocation,
284                                    gpointer               user_data)
285 {
286     LoggerD("entered\n\tsender=" << sender << "\n\tobject_path=" << object_path << "\n\tinterface_name=" << interface_name << "\n\tmethod_name=" << method_name);
287
288     Bluez *ctx = static_cast<Bluez*>(user_data);
289     if(!ctx) {
290         LoggerD("Failed to cast to Bluez");
291         g_dbus_method_invocation_return_value(invocation, NULL);
292         return;
293     }
294
295     if(!strcmp(method_name, "Authorize")) {
296         g_dbus_method_invocation_return_value(invocation, NULL);
297     }
298     else if(!strcmp(method_name, "RequestPinCode")) {
299         g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", AGENT_PINCODE));
300     }
301     else if(!strcmp(method_name, "RequestPasskey")) {
302         g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", AGENT_PASSKEY));
303     }
304     else if (!strcmp(method_name, "Release")) {
305         if(!strcmp(object_path, AGENT_PATH)) { // released agent for pairing
306             bool unregistered = g_dbus_connection_unregister_object(g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL), ctx->mAgentRegistrationId);
307             if(unregistered)
308                 ctx->mAgentRegistrationId = -1;
309         }
310         g_dbus_method_invocation_return_value(invocation, NULL);
311     }
312     else {
313         // DisplayPasskey, DisplayPinCode, RequestConfirmation, ConfirmModeChange, Cancel
314         g_dbus_method_invocation_return_value(invocation, NULL);
315     }
316 }
317
318 bool Bluez::isDevicePaired(const char *bt_addr) {
319     bool paired = false;
320
321     if(!mAdapterPath)
322         return paired;
323
324     GError *err = NULL;
325     GVariant *reply = NULL;
326     reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
327                                          BLUEZ_SERVICE,
328                                          mAdapterPath,
329                                          BLUEZ_ADAPTER_IFACE,
330                                          "FindDevice",
331                                          g_variant_new("(s)", bt_addr),
332                                          NULL,
333                                          G_DBUS_CALL_FLAGS_NONE,
334                                          -1,
335                                          NULL,
336                                          &err);
337
338     if(err || !reply) {
339         if(err)
340             g_error_free(err);
341         return paired;
342     }
343
344     const char *tmp = NULL;
345     g_variant_get(reply, "(o)", &tmp);
346     if(!tmp) {
347         g_variant_unref(reply);
348         return paired;
349     }
350
351     char *device = strdup(tmp);
352     g_variant_unref(reply);
353
354     // get device properties and check if the device is Paired
355     reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
356                                          BLUEZ_SERVICE,
357                                          device,
358                                          BLUEZ_DEVICE_IFACE,
359                                          "GetProperties",
360                                          NULL,
361                                          NULL,
362                                          G_DBUS_CALL_FLAGS_NONE,
363                                          -1,
364                                          NULL,
365                                          &err);
366     if(err || !reply) {
367         if(err)
368             g_error_free(err);
369         free(device);
370         return paired;
371     }
372
373     GVariantIter *iter;
374     g_variant_get(reply, "(a{sv})", &iter);
375     const char *key;
376     GVariant *value;
377     while(g_variant_iter_next(iter, "{sv}", &key, &value)) {
378         if(!strcmp(key, "Paired")) {
379             paired = g_variant_get_boolean(value);
380             break;
381         }
382     }
383
384     free(device);
385     g_variant_unref(reply);
386
387     return paired;
388 }
389
390 void Bluez::setupAgent()
391 {
392     LoggerD("entered: registering agent " << AGENT_PATH);
393
394     /*
395     if(mAgentRegistrationId > 0) { // alread registered
396         LoggerD("Bluez agent registered");
397         return;
398     }
399     */
400
401     mAgentIfaceVTable.method_call = Bluez::agentHandleMethodCall;
402     mAgentIntrospectionData = g_dbus_node_info_new_for_xml(AGENT_INTERFACE_XML, NULL);
403
404     if (mAgentIntrospectionData == NULL) {
405         LoggerD("failed to create introspection data.");
406         return;
407     }
408     LoggerD("introspection data parsed OK");
409
410     GError *err = NULL;
411     mAgentRegistrationId = g_dbus_connection_register_object( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
412                                                   AGENT_PATH,
413                                                   mAgentIntrospectionData->interfaces[0],
414                                                   &mAgentIfaceVTable, //const GDBusInterfaceVTable *vtable,
415                                                   this, //user_data
416                                                   NULL, //GDestroyNotify
417                                                   &err);
418
419     if(err) {
420         LoggerD("Failed to register object: " << AGENT_PATH << " : " << err->message);
421         g_error_free(err);
422         return;
423     }
424     LoggerD("object registered with id=" << mAgentRegistrationId);
425 }
426
427 void Bluez::registerAgent()
428 {
429     LoggerD("entered");
430
431     GError *err = NULL;
432     g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL),
433                                  BLUEZ_SERVICE,
434                                  mAdapterPath,
435                                  BLUEZ_ADAPTER_IFACE,
436                                  "RegisterAgent",
437                                  g_variant_new("(os)", AGENT_PATH, AGENT_CAPABILITIES), // floating variants are consumed
438                                  NULL,
439                                  G_DBUS_CALL_FLAGS_NONE,
440                                  -1,
441                                  NULL,
442                                  &err);
443     if(err) {
444         LoggerE("Failed to register agent: " << err->message);
445         g_error_free(err);
446         return;
447     }
448
449     LoggerD("Agent registered");
450 }
451
452 } // PhoneD
453