Imported Upstream version 1.1.0
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / bt_le_adapter / linux / peripheral.c
1 /* ****************************************************************
2  *
3  * Copyright 2015 Intel Corporation All Rights Reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  ******************************************************************/
18
19 #include "peripheral.h"
20 #include "utils.h"
21 #include "bluez.h"
22 #include "service.h"
23
24 #include "oic_malloc.h"
25 #include "logger.h"
26
27 #include <string.h>
28 #include <assert.h>
29
30
31 #define MICROSECS_PER_SEC 1000000
32
33 // Logging tag.
34 static char const TAG[] = "BLE_PERIPHERAL";
35
36 static CAPeripheralContext g_context = {
37     .lock = NULL
38 };
39
40 static bool CAPeripheralCheckStarted()
41 {
42     ca_mutex_lock(g_context.lock);
43
44     bool const started =
45         (g_context.event_loop != NULL
46          && g_main_loop_is_running(g_context.event_loop));
47
48     ca_mutex_unlock(g_context.lock);
49
50     /**
51      * @todo Fix potential TOCTOU race condition.  A peripheral could
52      *       have been started or stopped between the mutex unlock and
53      *       boolean check.
54      */
55     return started;
56 }
57
58
59 static bool CAPeripheralAdaptersFound(CALEContext * context)
60 {
61     // Check if BlueZ detected bluetooth hardware adapters.
62     ca_mutex_lock(context->lock);
63
64     bool const found = (context->adapters != NULL);
65
66     ca_mutex_unlock(context->lock);
67
68     if (!found)
69     {
70         OIC_LOG(WARNING, TAG, "No bluetooth hardware found.");
71     }
72
73     return found;
74 }
75
76 static void CAPeripheralDestroyGattServices(gpointer data)
77 {
78     CAGattService * const service = data;
79
80     CAGattServiceDestroy(service);
81
82     OICFree(service);
83 }
84
85 static GList * CAPeripheralInitializeGattServices(CALEContext * context)
86 {
87     GList * result = NULL;
88
89     /*
90       Create a proxies to the org.bluez.GattManager1 D-Bus objects that
91       will later be used to register the OIC GATT service.
92     */
93     GList * gatt_managers = NULL;
94     if (!CAGetBlueZManagedObjectProxies(&gatt_managers,
95                                         BLUEZ_GATT_MANAGER_INTERFACE,
96                                         context,
97                                         NULL))
98         return result;
99
100     GList * gatt_services = NULL;
101
102     for (GList * l = gatt_managers; l != NULL; )
103     {
104         CAGattService * const service = OICCalloc(1, sizeof(*service));
105
106         if (service == NULL)
107         {
108             g_list_free_full(gatt_services,
109                              CAPeripheralDestroyGattServices);
110             g_list_free_full(gatt_managers, g_object_unref);
111             return result;
112         }
113
114         /*
115           Use the HCI device name (e.g. hci0) in GattManager1 object
116           path (e.g. /org/bluez/hci0) as a means to differentiate the
117           GATT service hierarchy for each GattManager1 object.
118         */
119         GDBusProxy * const manager = G_DBUS_PROXY(l->data);
120         char const * const path = g_dbus_proxy_get_object_path(manager);
121
122         /*
123           The return value will actually be a pointer to a substring
124           starting with the '/' before the HCI name.  We add 1 later
125           on to skip that character.
126         */
127         char const * const hci_name = strrchr(path, '/');
128
129         if (hci_name == NULL
130             || !CAGattServiceInitialize(service, hci_name + 1, context))
131         {
132             g_list_free_full(gatt_services,
133                              CAPeripheralDestroyGattServices);
134             g_list_free_full(gatt_managers, g_object_unref);
135             return result;
136         }
137
138         service->gatt_manager = manager;
139
140         /*
141           The GattManager1 proxy is now owned by the CAGattService
142           object.
143         */
144
145         GList * const tmp = l;
146         l = l->next;
147         gatt_managers = g_list_delete_link(gatt_managers, tmp);
148
149         // Prepend for efficiency.
150         gatt_services = g_list_prepend(gatt_services, service);
151     }
152
153     result = gatt_services;
154
155     return result;
156 }
157
158 static bool CAPeripheralRegisterGattServices(
159     CAPeripheralContext * context)
160 {
161     assert(context != NULL);
162
163     bool success = true;
164
165     ca_mutex_lock(context->lock);
166
167     for (GList * l = context->gatt_services; l != NULL; l = l->next)
168     {
169         CAGattService * const service = l->data;
170
171         // Register the OIC service with the corresponding BlueZ Gatt
172         // Manager.
173
174         /*
175           org.bluez.GattManager1.RegisterService() accepts two
176           parameters: the service object path, and an options
177           dictionary.  No options are used so pass a NULL pointer to
178           reflect an empty dictionary.
179         */
180         GVariant * const parameters =
181             g_variant_new("(oa{sv})", service->object_path, NULL);
182
183         GError * error = NULL;
184
185         GVariant * const ret =
186             g_dbus_proxy_call_sync(
187                 service->gatt_manager,
188                 "RegisterService",
189                 parameters,
190                 G_DBUS_CALL_FLAGS_NONE,
191                 -1,    // timeout (default == -1),
192                 NULL,  // cancellable
193                 &error);
194
195         if (ret == NULL)
196         {
197             OIC_LOG_V(ERROR,
198                       TAG,
199                       "GATT service registration failed: %s",
200                       error->message);
201
202             g_error_free(error);
203
204             success = false;
205
206             break;
207         }
208
209         g_variant_unref(ret);
210     }
211
212     ca_mutex_unlock(context->lock);
213
214     return success;
215 }
216
217 static bool CAPeripheralRegisterAdvertisements(
218     CAPeripheralContext * context)
219 {
220     bool success = false;
221
222     /*
223       Register the OIC LE advertisement service with each BlueZ
224       LE Advertisement Manager.
225     */
226
227     ca_mutex_lock(context->lock);
228
229     char const * const advertisement_path =
230         g_dbus_interface_skeleton_get_object_path(
231             G_DBUS_INTERFACE_SKELETON(
232                 context->advertisement.advertisement));
233
234     GList * managers = context->advertisement.managers;
235
236     for (GList * l = managers; l != NULL; )
237     {
238         GDBusProxy * const manager = G_DBUS_PROXY(l->data);
239
240         /**
241          * @c org.bluez.LEAdvertisingManager1.RegisterService()
242          * accepts two parameters: the advertisement object path, and
243          * an options dictionary.  No options are used so pass a NULL
244          * pointer to reflect an empty dictionary.
245          *
246          *  @todo The parameters don't change between loop iterations.
247          *        Ideally we should initialize this variable before
248          *        the loop is executed, but g_dbus_proxy_call_sync()
249          *        takes ownership of the variant.  Is there anyway to
250          *        create a non-floating GVariant and pass it to that
251          *        function?
252          */
253         GVariant * const parameters =
254             g_variant_new("(oa{sv})", advertisement_path, NULL);
255
256         GError * error = NULL;
257
258         GVariant * const ret =
259             g_dbus_proxy_call_sync(
260                 manager,
261                 "RegisterAdvertisement",
262                 parameters,
263                 G_DBUS_CALL_FLAGS_NONE,
264                 -1,    // timeout (default == -1),
265                 NULL,  // cancellable
266                 &error);
267
268         if (ret == NULL)
269         {
270             OIC_LOG_V(WARNING,
271                       TAG,
272                       "LE advertisement registration on %s failed: %s",
273                       g_dbus_proxy_get_object_path(manager),
274                       error->message);
275
276             g_error_free(error);
277
278             // We can't use the LE advertising manager.  Drop it.
279             g_object_unref(manager);
280
281             GList * const tmp = l;
282             l = l->next;
283             managers = g_list_delete_link(managers, tmp);
284
285             continue;
286         }
287
288         g_variant_unref(ret);
289
290         /*
291           Note that we can do this in the for-statement because of
292           the similar code in the error case above.
293         */
294         l = l->next;
295     }
296
297     // Use the updated list of managers.
298     context->advertisement.managers = managers;
299
300     if (managers == NULL)   // Empty
301     {
302         OIC_LOG(ERROR,
303                 TAG,
304                 "LE advertisment registration failed for all "
305                 "Bluetooth adapters.");
306     }
307     else
308     {
309
310         success = true;
311     }
312
313     ca_mutex_unlock(context->lock);
314
315     return success;
316 }
317
318 static void CAPeripheralSetDiscoverable(gpointer data,
319                                         gpointer user_data,
320                                         gboolean discoverable)
321 {
322     assert(data != NULL);
323     assert(user_data != NULL);
324
325     GDBusProxy * const adapter = G_DBUS_PROXY(data);
326     CAResult_t * const result = (CAResult_t *) user_data;
327     *result = CA_STATUS_FAILED;
328
329     /*
330       Make sure the adapter is powered on before making it
331       discoverable.
332
333       @todo We used to power off the adapter once we're done with it,
334             but that isn't always desirable.  We should only power it
335             off if it was off prior to us powering it on.
336     */
337     if (discoverable
338         && !CASetBlueZObjectProperty(adapter,
339                                   BLUEZ_ADAPTER_INTERFACE,
340                                   "Powered",
341                                   g_variant_new_boolean(discoverable)))
342     {
343         OIC_LOG_V(ERROR,
344                   TAG,
345                   "Unable to power %s LE peripheral adapter.",
346                   discoverable ? "on" : "off");
347
348         return;
349     }
350
351     /**
352      * @note Enabling LE advertising used to be done here using the
353      *       kernel bluetooth management API.  However, we now
354      *       leverage the BlueZ LE Advertisment D-Bus API instead
355      *       since it handles all of the desired advertising
356      *       operations without need of the calling process to have
357      *       @c CAP_NET_ADMIN capabilities.  Advertisment registration
358      *       is performed in this source file.
359      */
360
361     *result = CA_STATUS_OK;
362 }
363
364 static void CAPeripheralMakeDiscoverable(gpointer adapter,
365                                          gpointer result)
366 {
367     CAPeripheralSetDiscoverable(adapter, result, TRUE);
368 }
369
370 static void CAPeripheralMakeUndiscoverable(gpointer adapter,
371                                            gpointer result)
372 {
373     CAPeripheralSetDiscoverable(adapter, result, FALSE);
374 }
375
376 static CAResult_t CAPeripheralSetDiscoverability(
377     CALEContext * context,
378     GFunc discoverability_func)
379 {
380     CAResult_t result = CA_STATUS_FAILED;
381
382     /*
383       Synchronize access to the adapter information using the base
384       context lock since we don't own the adapter_infos.
385      */
386     ca_mutex_lock(context->lock);
387
388     // Make all detected adapters discoverable.
389     g_list_foreach(context->adapters,
390                    discoverability_func,
391                    &result);
392
393     ca_mutex_unlock(context->lock);
394
395     return result;
396 }
397
398 /**
399  * Callback function invoked when a D-Bus bus name is acquired.
400  *
401  * @param[in] connection The D-Bus connection on which the name was
402  *                       acquired.
403  * @param[in] name       The bus name that was acquired.
404  * @param[in] user_data  User-provided data.
405  */
406 static void CAPeripheralOnNameAcquired(GDBusConnection * connection,
407                                        gchar const * name,
408                                        gpointer user_data)
409 {
410     (void)connection;
411     (void)name; // needed when logging is a noop
412     (void)user_data;
413     OIC_LOG_V(DEBUG,
414               TAG,
415               "Name \"%s\" acquired on D-Bus.", name);
416 }
417
418 /**
419  * Callback function invoked when a D-Bus bus name is no longer owned
420  * or the D-Bus connection has been closed.
421  *
422  * @param[in] connection The D-Bus connection on which the bus name
423  *                       should have been acquired.
424  * @param[in] name       The bus name that was not acquired.
425  * @param[in] user_data  User-provided data.
426  */
427 static void CAPeripheralOnNameLost(GDBusConnection * connection,
428                                    gchar const * name,
429                                    gpointer user_data)
430 {
431     (void)connection;
432     (void)name; // needed when logging is a noop
433     (void)user_data;
434     /*
435       This can happen if the appropriate D-Bus policy is not
436       installed, for example.
437     */
438     OIC_LOG_V(WARNING,
439               TAG,
440               "Lost name \"%s\" on D-Bus!", name);
441 }
442
443 /**
444  * Inform thread waiting for the event loop to start that the loop has
445  * started.  This is done in the context of the event loop itself so
446  * that we can be certain that the event loop is indeed running.
447  */
448 static gboolean CAPeripheralEventLoopStarted(gpointer user_data)
449 {
450     ca_cond const condition = user_data;
451
452     ca_cond_signal(condition);  // For service registration
453
454     return G_SOURCE_REMOVE;
455 }
456
457 static void CAPeripheralStartEventLoop(void * data)
458 {
459     CALEContext * const context = data;
460
461     assert(context != NULL);
462
463     // Create the event loop.
464     GMainContext * const loop_context = g_main_context_new();
465     GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE);
466
467     g_main_context_push_thread_default(loop_context);
468
469     // Acquire the bus name after exporting our D-Bus objects.
470     guint const owner_id =
471         g_bus_own_name_on_connection(context->connection,
472                                      CA_DBUS_GATT_SERVICE_NAME,
473                                      G_BUS_NAME_OWNER_FLAGS_NONE,
474                                      CAPeripheralOnNameAcquired,
475                                      CAPeripheralOnNameLost,
476                                      NULL, // user_data,
477                                      NULL);
478
479     /**
480      * Create proxies to the @c org.bluez.LEAdvertisingManager1 D-Bus
481      * objects that will later be used to register the OIC LE
482      * advertisement data.
483      *
484      * @todo Failure to retrieve the LE advertising managers is
485      *       currently ignored.  We should propagate the failure to
486      *       the thread that spawned this one.
487      *
488      * @note Retrieval of the @c org.bluez.LEAdvertisingManager1
489      *       proxies must be done in a thread separate from the one
490      *       that makes calls through those proxies since the
491      *       underlying GDBusObjectManagerClient sets up signal
492      *       subscriptions that are used when dispatching D-Bus method
493      *       handling calls (e.g. property retrieval, etc).
494      *       Otherwise, a distributed deadlock situation could occur
495      *       if a synchronous D-Bus proxy call is made that causes the
496      *       recipient (like BlueZ) to call back in to the thread that
497      *       handles signals.  For example, registration of our LE
498      *       advertisment with BlueZ causes BlueZ itself to make a
499      *       call to our own @c org.bluez.LEAdvertisement1 object.
500      *       However, the thread that initiated the advertisement
501      *       registration is blocked waiting for BlueZ to respond, but
502      *       BlueZ is blocked waiting for that same thread to respond
503      *       to its own advertisement property retrieval call.
504      */
505     GList * advertising_managers = NULL;
506     if (!CAGetBlueZManagedObjectProxies(
507             &advertising_managers,
508             BLUEZ_ADVERTISING_MANAGER_INTERFACE,
509             context,
510             NULL))
511     {
512         OIC_LOG(ERROR,
513                 TAG,
514                 "Failed to retrieve BlueZ LE advertising "
515                 "manager interface.");
516     }
517
518     ca_mutex_lock(g_context.lock);
519
520     assert(g_context.event_loop == NULL);
521     g_context.event_loop = event_loop;
522
523     g_context.base = context;
524
525     g_context.owner_id = owner_id;
526
527     /**
528      * Initialize all GATT services.
529      *
530      * @todo Failure to initialize the OIC GATT services is currently
531      *       ignored.  We should propagate the failure to the thread
532      *       that spawned this one.
533      *
534      * @note See the @c org.bluez.LEAdvertisingManager1 note above to
535      *       understand why the GATT services must be initialized in
536      *       a thread seperate from the one that initiates GATT
537      *       service registration.
538      */
539     g_context.gatt_services = CAPeripheralInitializeGattServices(context);
540
541     CALEAdvertisementInitialize(&g_context.advertisement,
542                                 context->connection,
543                                 advertising_managers);
544
545     ca_mutex_unlock(g_context.lock);
546
547     /*
548       Add an idle handler that notifies a thread waiting for the
549       GLib event loop to run that the event loop is actually
550       running.  We do this in the context of the event loop itself
551       to avoid race conditions.
552     */
553     GSource * const source = g_idle_source_new();
554     g_source_set_priority(source, G_PRIORITY_HIGH_IDLE);
555     g_source_set_callback(source,
556                           CAPeripheralEventLoopStarted,
557                           g_context.condition,  // data
558                           NULL);                // notify
559     (void) g_source_attach(source, loop_context);
560     g_source_unref(source);
561
562     g_main_loop_run(event_loop);  // Blocks until loop is quit.
563
564     /*
565       Clean up in the same thread to avoid having to explicitly bump
566       the ref count to retain ownership.
567     */
568     g_main_context_unref(loop_context);
569     g_main_loop_unref(event_loop);
570 }
571
572 static void CAPeripheralStopEventLoop(CAPeripheralContext * context)
573 {
574     ca_mutex_lock(context->lock);
575
576     GMainLoop * const event_loop = context->event_loop;
577     context->event_loop = NULL;
578
579     ca_mutex_unlock(context->lock);
580
581     if (event_loop != NULL)
582     {
583         g_main_loop_quit(event_loop);
584
585         GMainContext * const loop_context =
586             g_main_loop_get_context(event_loop);
587
588         if (loop_context != NULL)
589         {
590             g_main_context_wakeup(loop_context);
591         }
592     }
593 }
594
595 // ------------------------------------------------------
596
597 void CAPeripheralInitialize()
598 {
599     g_context.lock      = ca_mutex_new();
600     g_context.condition = ca_cond_new();
601 }
602
603 void CAPeripheralFinalize()
604 {
605     ca_cond_free(g_context.condition);
606     ca_mutex_free(g_context.lock);
607 }
608
609 CAResult_t CAPeripheralStart(CALEContext * context)
610 {
611     /**
612      * @todo Bluetooth adapters that are hot-plugged after the
613      *       peripheral has started will not be started!
614      */
615
616     CAResult_t result = CA_STATUS_FAILED;
617
618     // Only start if we were previously stopped.
619     if (CAPeripheralCheckStarted())
620     {
621         result = CA_SERVER_STARTED_ALREADY;
622         return result;
623     }
624
625     if (!CAPeripheralAdaptersFound(context))
626     {
627         // No Bluetooth adapters.  Don't bother continuing.
628         return result;
629     }
630
631     /*
632       Spawn a thread to run the Glib event loop that will drive D-Bus
633       signal handling.
634      */
635     result = ca_thread_pool_add_task(context->server_thread_pool,
636                                      CAPeripheralStartEventLoop,
637                                      context);
638
639     if (result != CA_STATUS_OK)
640     {
641         return result;
642     }
643
644     /*
645       Wait until initialization completes and the event loop is up and
646       running before proceeding to service and advertisement
647       registration.
648     */
649
650     // Number of times to wait for initialization to complete.
651     static int const max_retries = 2;
652
653     static uint64_t const timeout =
654         2 * MICROSECS_PER_SEC;  // Microseconds
655
656     ca_mutex_lock(g_context.lock);
657
658     for (int i = 0;
659          g_context.gatt_services == NULL && i < max_retries;
660          ++i)
661     {
662         if (ca_cond_wait_for(g_context.condition,
663                              g_context.lock,
664                              timeout) == CA_WAIT_SUCCESS)
665         {
666             result = CA_STATUS_OK;
667         }
668     }
669
670     ca_mutex_unlock(g_context.lock);
671
672     if (result != CA_STATUS_OK)
673     {
674         return result;
675     }
676
677     /**
678      * First register the GATT services, then register the LE
679      * advertisments with BlueZ to make sure the service we're
680      * advertising actually exists.
681      */
682     if (result == CA_STATUS_OK
683         && !(CAPeripheralRegisterGattServices(&g_context)
684              && CAPeripheralRegisterAdvertisements(&g_context)))
685     {
686         result = CA_STATUS_FAILED;
687     }
688
689     /*
690       Make the local bluetooth adapters discoverable over LE by
691       enabling LE, enabling advertising, and making the LE device
692       connectable.
693     */
694     result = CAPeripheralSetDiscoverability(context,
695                                             CAPeripheralMakeDiscoverable);
696
697     return result;
698 }
699
700 CAResult_t CAPeripheralStop()
701 {
702     CAResult_t result = CA_STATUS_FAILED;
703
704     // Only stop if we were previously started.
705     if (!CAPeripheralCheckStarted())
706     {
707         return result;
708     }
709
710     /*
711       Make the local bluetooth adapters undiscoverable.
712
713       This function also sets the base context to NULL.
714     */
715     result =
716         CAPeripheralSetDiscoverability(g_context.base,
717                                        CAPeripheralMakeUndiscoverable);
718
719     CAPeripheralStopEventLoop(&g_context);
720
721     ca_mutex_lock(g_context.lock);
722
723     guint const owner_id = g_context.owner_id;
724     g_context.owner_id = 0;
725
726     GList * const gatt_services = g_context.gatt_services;
727     g_context.gatt_services = NULL;
728
729     g_context.base = NULL;
730
731     ca_mutex_unlock(g_context.lock);
732
733     CALEAdvertisementDestroy(&g_context.advertisement);
734
735     g_list_free_full(gatt_services, CAPeripheralDestroyGattServices);
736
737     g_bus_unown_name(owner_id);
738
739     return result;
740 }
741
742 void CAPeripheralForEachService(GFunc func, void * user_data)
743 {
744     ca_mutex_lock(g_context.lock);
745
746     g_list_foreach(g_context.gatt_services, func, user_data);
747
748     ca_mutex_unlock(g_context.lock);
749 }