Imported Upstream version 1.0.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 #include "characteristic.h"
24 #include "descriptor.h"
25
26 #include "oic_malloc.h"
27 #include "logger.h"
28
29 #include <string.h>
30 #include <assert.h>
31
32
33 #define MICROSECS_PER_SEC 1000000
34
35 // Logging tag.
36 static char const TAG[] = "BLE_PERIPHERAL";
37
38 static CAPeripheralContext g_context = {
39     .lock = NULL
40 };
41
42 static bool CAPeripheralCheckStarted()
43 {
44     ca_mutex_lock(g_context.lock);
45
46     bool const started = (g_context.base != NULL);
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, context, hci_name + 1))
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 proxies are now owned by the CAGattService
142           objects.
143         */
144         GList * const tmp = l;
145         l = l->next;
146         gatt_managers = g_list_delete_link(gatt_managers, tmp);
147
148         // Prepend for efficiency.
149         gatt_services = g_list_prepend(gatt_services, service);
150     }
151
152     result = gatt_services;
153
154     return result;
155 }
156
157 static bool CAPeripheralRegisterGattServices(
158     CAPeripheralContext * context)
159 {
160     assert(context != NULL);
161
162     bool success = false;
163
164     ca_mutex_lock(context->lock);
165
166     for (GList * l = context->gatt_services; l != NULL; l = l->next)
167     {
168         CAGattService * const service = l->data;
169
170         // Register the OIC service with the corresponding BlueZ Gatt
171         // Manager.
172
173         /*
174           org.bluez.GattManager1.RegisterService() accepts two
175           parameters: the service object path, and an options
176           dictionary.  No options are used so pass a NULL pointer to
177           reflect an empty dictionary.
178         */
179         GVariant * const parameters =
180             g_variant_new("(oa{sv})", service->object_path, NULL);
181
182         GError * error = NULL;
183
184         GVariant * const ret =
185             g_dbus_proxy_call_sync(
186                 service->gatt_manager,
187                 "RegisterService",
188                 parameters,
189                 G_DBUS_CALL_FLAGS_NONE,
190                 -1,    // timeout (default == -1),
191                 NULL,  // cancellable
192                 &error);
193
194         if (ret == NULL)
195         {
196             OIC_LOG_V(ERROR,
197                       TAG,
198                       "GATT service registration failed: %s",
199                       error->message);
200
201             g_error_free(error);
202
203             success = false;
204
205             break;
206         }
207
208         g_variant_unref(ret);
209     }
210
211     ca_mutex_unlock(context->lock);
212
213     success = true;
214
215     return success;
216 }
217
218 static bool CAPeripheralRegisterAdvertisements(
219     CAPeripheralContext * context)
220 {
221     bool success = false;
222
223     /*
224       Register the OIC LE advertisement service with each BlueZ
225       LE Advertisement Manager.
226     */
227
228     ca_mutex_lock(context->lock);
229
230     char const * const advertisement_path =
231         g_dbus_interface_skeleton_get_object_path(
232             G_DBUS_INTERFACE_SKELETON(
233                 context->advertisement.advertisement));
234
235     GList * managers = context->advertisement.managers;
236
237     for (GList * l = managers; l != NULL; )
238     {
239         GDBusProxy * const manager = G_DBUS_PROXY(l->data);
240
241         /**
242          * @c org.bluez.LEAdvertisingManager1.RegisterService()
243          * accepts two parameters: the advertisement object path, and
244          * an options dictionary.  No options are used so pass a NULL
245          * pointer to reflect an empty dictionary.
246          *
247          *  @todo The parameters don't change between loop iterations.
248          *        Ideally we should initialize this variable before
249          *        the loop is executed, but g_dbus_proxy_call_sync()
250          *        takes ownership of the variant.  Is there anyway to
251          *        create a non-floating GVariant and pass it to that
252          *        function?
253          */
254         GVariant * const parameters =
255             g_variant_new("(oa{sv})", advertisement_path, NULL);
256
257         GError * error = NULL;
258
259         GVariant * const ret =
260             g_dbus_proxy_call_sync(
261                 manager,
262                 "RegisterAdvertisement",
263                 parameters,
264                 G_DBUS_CALL_FLAGS_NONE,
265                 -1,    // timeout (default == -1),
266                 NULL,  // cancellable
267                 &error);
268
269         if (ret == NULL)
270         {
271             OIC_LOG_V(WARNING,
272                       TAG,
273                       "LE advertisement registration on %s failed: %s",
274                       g_dbus_proxy_get_object_path(manager),
275                       error->message);
276
277             g_error_free(error);
278
279             // We can't use the LE advertising manager.  Drop it.
280             g_object_unref(manager);
281
282             GList * const tmp = l;
283             l = l->next;
284             managers = g_list_delete_link(managers, tmp);
285
286             continue;
287         }
288
289         g_variant_unref(ret);
290
291         /*
292           Note that we can do this in the for-statement because of
293           the similar code in the error case above.
294         */
295         l = l->next;
296     }
297
298     // Use the updated list of managers.
299     context->advertisement.managers = managers;
300
301     if (managers == NULL)   // Empty
302     {
303         OIC_LOG(ERROR,
304                 TAG,
305                 "LE advertisment registration failed for all "
306                 "Bluetooth adapters.");
307     }
308     else
309     {
310
311         success = true;
312     }
313
314     ca_mutex_unlock(context->lock);
315
316     return success;
317 }
318
319 static void CAPeripheralSetDiscoverable(gpointer data,
320                                         gpointer user_data,
321                                         gboolean discoverable)
322 {
323     assert(data != NULL);
324     assert(user_data != NULL);
325
326     GDBusProxy * const adapter = G_DBUS_PROXY(data);
327     CAResult_t * const result = (CAResult_t *) user_data;
328     *result = CA_STATUS_FAILED;
329
330     /*
331       Make sure the adapter is powered on before making it
332       discoverable.
333     */
334     if (!CASetBlueZObjectProperty(adapter,
335                                   BLUEZ_ADAPTER_INTERFACE,
336                                   "Powered",
337                                   g_variant_new_boolean(discoverable)))
338     {
339         OIC_LOG_V(ERROR,
340                   TAG,
341                   "Unable to power %s LE peripheral adapter.",
342                   discoverable ? "on" : "off");
343
344         return;
345     }
346
347     /**
348      * @note Enabling LE advertising used to be done here using the
349      *       kernel bluetooth management API.  However, we now
350      *       leverage the BlueZ LE Advertisment D-Bus API instead
351      *       since it handles all of the desired advertising
352      *       operations without need of the calling process to have
353      *       @c CAP_NET_ADMIN capabilities.  Advertisment registration
354      *       is performed in this source file.
355      */
356
357     *result = CA_STATUS_OK;
358 }
359
360 static void CAPeripheralMakeDiscoverable(gpointer adapter,
361                                          gpointer result)
362 {
363     CAPeripheralSetDiscoverable(adapter, result, TRUE);
364 }
365
366 static void CAPeripheralMakeUndiscoverable(gpointer adapter,
367                                            gpointer result)
368 {
369     CAPeripheralSetDiscoverable(adapter, result, FALSE);
370 }
371
372 static CAResult_t CAPeripheralSetDiscoverability(
373     CALEContext * context,
374     GFunc discoverability_func)
375 {
376     CAResult_t result = CA_STATUS_FAILED;
377
378     /*
379       Synchronize access to the adapter information using the base
380       context lock since we don't own the adapter_infos.
381      */
382     ca_mutex_lock(context->lock);
383
384     // Make all detected adapters discoverable.
385     g_list_foreach(context->adapters,
386                    discoverability_func,
387                    &result);
388
389     ca_mutex_unlock(context->lock);
390
391     return result;
392 }
393
394 /**
395  * Callback function invoked when a D-Bus bus name is acquired.
396  *
397  * @param[in] connection The D-Bus connection on which the name was
398  *                       acquired.
399  * @param[in] name       The bus name that was acquired.
400  * @param[in] user_data  User-provided data.
401  */
402 static void CAPeripheralOnNameAcquired(GDBusConnection * connection,
403                                        gchar const * name,
404                                        gpointer user_data)
405 {
406     (void)connection;
407     (void)name; // needed when logging is a noop
408     (void)user_data;
409     OIC_LOG_V(DEBUG,
410               TAG,
411               "Name \"%s\" acquired on D-Bus.", name);
412 }
413
414 /**
415  * Callback function invoked when a D-Bus bus name is no longer owned
416  * or the D-Bus connection has been closed.
417  *
418  * @param[in] connection The D-Bus connection on which the bus name
419  *                       should have been acquired.
420  * @param[in] name       The bus name that was not acquired.
421  * @param[in] user_data  User-provided data.
422  */
423 static void CAPeripheralOnNameLost(GDBusConnection * connection,
424                                    gchar const * name,
425                                    gpointer user_data)
426 {
427     (void)connection;
428     (void)name; // needed when logging is a noop
429     (void)user_data;
430     /*
431       This can happen if the appropriate D-Bus policy is not
432       installed, for example.
433     */
434     OIC_LOG_V(WARNING,
435               TAG,
436               "Lost name \"%s\" on D-Bus!", name);
437 }
438
439 static void CAPeripheralStartEventLoop(void * data)
440 {
441     CALEContext * const context = data;
442
443     assert(context != NULL);
444
445     // Create the event loop.
446     GMainContext * const loop_context = g_main_context_new();
447     GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE);
448
449     g_main_context_push_thread_default(loop_context);
450
451     // Acquire the bus name after exporting our D-Bus objects.
452     guint const owner_id =
453         g_bus_own_name_on_connection(context->connection,
454                                      CA_DBUS_GATT_SERVICE_NAME,
455                                      G_BUS_NAME_OWNER_FLAGS_NONE,
456                                      CAPeripheralOnNameAcquired,
457                                      CAPeripheralOnNameLost,
458                                      NULL, // user_data,
459                                      NULL);
460
461     /**
462      * Create proxies to the @c org.bluez.LEAdvertisingManager1 D-Bus
463      * objects that will later be used to register the OIC LE
464      * advertisement data.
465      *
466      * @todo Failure to retrieve the LE advertising managers is
467      *       currently ignored.  We should propagate the failure to
468      *       the thread that spawned this one.
469      *
470      * @note Retrieval of the @c org.bluez.LEAdvertisingManager1
471      *       proxies must be done in a thread separate from the one
472      *       that makes calls through those proxies since the
473      *       underlying GDBusObjectManagerClient sets up signal
474      *       subscriptions that are used when dispatching D-Bus method
475      *       handling calls (e.g. property retrieval, etc).
476      *       Otherwise, a distributed deadlock situation could occur
477      *       if a synchronous D-Bus proxy call is made that causes the
478      *       recipient (like BlueZ) to call back in to the thread that
479      *       handles signals.  For example, registration of our LE
480      *       advertisment with BlueZ causes BlueZ itself to make a
481      *       call to our own @c org.bluez.LEAdvertisement1 object.
482      *       However, the thread that initiated the advertisement
483      *       registration is blocked waiting for BlueZ to respond, but
484      *       BlueZ is blocked waiting for that same thread to respond
485      *       to its own advertisement property retrieval call.
486      */
487     GList * advertising_managers = NULL;
488     if (!CAGetBlueZManagedObjectProxies(
489             &advertising_managers,
490             BLUEZ_ADVERTISING_MANAGER_INTERFACE,
491             context,
492             NULL))
493     {
494         OIC_LOG(ERROR,
495                 TAG,
496                 "Failed to retrieve BlueZ LE advertising "
497                 "manager interface.");
498     }
499
500     /**
501      * Initialize all GATT services.
502      *
503      * @todo Failure to initialize the OIC GATT services is currently
504      *       ignored.  We should propagate the failure to the thread
505      *       that spawned this one.
506      *
507      * @note See the @c org.bluez.LEAdvertisingManager1 note above to
508      *       understand why the GATT services must be initialized in
509      *       a thread seperate from the one that initiates GATT
510      *       service registration.
511      */
512     GList * const gatt_services =
513         CAPeripheralInitializeGattServices(context);
514
515     ca_mutex_lock(g_context.lock);
516
517     assert(g_context.event_loop == NULL);
518     g_context.event_loop = event_loop;
519
520     g_context.base = context;
521
522     g_context.owner_id = owner_id;
523
524     CALEAdvertisementInitialize(&g_context.advertisement,
525                                 context->connection,
526                                 advertising_managers);
527
528     g_context.gatt_services = gatt_services;
529
530     ca_mutex_unlock(g_context.lock);
531
532     ca_cond_signal(g_context.condition);
533
534     g_main_loop_run(event_loop);
535 }
536
537 static void CAPeripheralStopEventLoop(CAPeripheralContext * context)
538 {
539     ca_mutex_lock(context->lock);
540
541     GMainLoop * const event_loop = context->event_loop;
542     context->event_loop = NULL;
543
544     ca_mutex_unlock(context->lock);
545
546     if (event_loop != NULL)
547     {
548         g_main_loop_quit(event_loop);
549
550         GMainContext * const loop_context =
551             g_main_loop_get_context(event_loop);
552
553         if (loop_context != NULL)
554         {
555             g_main_context_wakeup(loop_context);
556             g_main_context_unref(loop_context);
557         }
558
559         g_main_loop_unref(event_loop);
560     }
561 }
562
563 // ------------------------------------------------------
564
565 void CAPeripheralInitialize()
566 {
567     g_context.lock      = ca_mutex_new();
568     g_context.condition = ca_cond_new();
569 }
570
571 void CAPeripheralFinalize()
572 {
573     ca_cond_free(g_context.condition);
574     ca_mutex_free(g_context.lock);
575 }
576
577 CAResult_t CAPeripheralStart(CALEContext * context)
578 {
579     /**
580      * @todo Bluetooth adapters that are hot-plugged after the
581      *       peripheral has started will not be started!
582      */
583
584     CAResult_t result = CA_STATUS_FAILED;
585
586     // Only start if we were previously stopped.
587     if (CAPeripheralCheckStarted())
588     {
589         result = CA_SERVER_STARTED_ALREADY;
590         return result;
591     }
592
593     if (!CAPeripheralAdaptersFound(context))
594     {
595         // No Bluetooth adapters.  Don't bother continuing.
596         return result;
597     }
598
599     /*
600       Spawn a thread to run the Glib event loop that will drive D-Bus
601       signal handling.
602      */
603     result = ca_thread_pool_add_task(context->server_thread_pool,
604                                      CAPeripheralStartEventLoop,
605                                      context);
606
607     if (result != CA_STATUS_OK)
608     {
609         return result;
610     }
611
612     /*
613       Wait until initialization completes before proceeding to
614       service and advertisement registration.
615     */
616
617     // Number of times to wait for initialization to complete.
618     static int const max_retries = 2;
619
620     static uint64_t const timeout =
621         2 * MICROSECS_PER_SEC;  // Microseconds
622
623     ca_mutex_lock(g_context.lock);
624
625     for (int i = 0;
626          g_context.gatt_services == NULL && i < max_retries;
627          ++i)
628     {
629         if (ca_cond_wait_for(g_context.condition,
630                              g_context.lock,
631                              timeout) == 0)
632         {
633             result = CA_STATUS_OK;
634         }
635     }
636
637     ca_mutex_unlock(g_context.lock);
638
639     if (result == CA_STATUS_FAILED)
640     {
641         return result;
642     }
643
644     /**
645      * First register the GATT services, then register the LE
646      * advertisments with BlueZ to make sure the service we're
647      * advertising actually exists.
648      */
649     if (result == CA_STATUS_OK
650         && !(CAPeripheralRegisterGattServices(&g_context)
651              && CAPeripheralRegisterAdvertisements(&g_context)))
652     {
653         result = CA_STATUS_FAILED;
654     }
655
656     /*
657       Make the local bluetooth adapters discoverable over LE by
658       enabling LE, enabling advertising, and making the LE device
659       connectable.
660     */
661     result = CAPeripheralSetDiscoverability(context,
662                                             CAPeripheralMakeDiscoverable);
663
664     return result;
665 }
666
667 CAResult_t CAPeripheralStop()
668 {
669     CAResult_t result = CA_STATUS_FAILED;
670
671     // Only stop if we were previously started.
672     if (!CAPeripheralCheckStarted())
673     {
674         result = CA_STATUS_OK;
675         return result;
676     }
677
678     /*
679       Make the local bluetooth adapters undiscoverable.
680
681       This function also sets the base context to NULL.
682     */
683     result =
684         CAPeripheralSetDiscoverability(g_context.base,
685                                        CAPeripheralMakeUndiscoverable);
686
687     CAPeripheralStopEventLoop(&g_context);
688
689     ca_mutex_lock(g_context.lock);
690
691     guint const owner_id = g_context.owner_id;
692     g_context.owner_id = 0;
693
694     GList * const gatt_services = g_context.gatt_services;
695     g_context.gatt_services = NULL;
696
697     g_context.base = NULL;
698
699     ca_mutex_unlock(g_context.lock);
700
701     CALEAdvertisementDestroy(&g_context.advertisement);
702
703     g_list_free_full(gatt_services, CAPeripheralDestroyGattServices);
704
705     g_bus_unown_name(owner_id);
706
707     return result;
708 }