Implemented Linux BLE transport.
[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     OIC_LOG_V(DEBUG,
407               TAG,
408               "Name \"%s\" acquired on D-Bus.", name);
409 }
410
411 /**
412  * Callback function invoked when a D-Bus bus name is no longer owned
413  * or the D-Bus connection has been closed.
414  *
415  * @param[in] connection The D-Bus connection on which the bus name
416  *                       should have been acquired.
417  * @param[in] name       The bus name that was not acquired.
418  * @param[in] user_data  User-provided data.
419  */
420 static void CAPeripheralOnNameLost(GDBusConnection * connection,
421                                    gchar const * name,
422                                    gpointer user_data)
423 {
424     /*
425       This can happen if the appropriate D-Bus policy is not
426       installed, for example.
427     */
428     OIC_LOG_V(WARNING,
429               TAG,
430               "Lost name \"%s\" on D-Bus!", name);
431 }
432
433 static void CAPeripheralStartEventLoop(void * data)
434 {
435     CALEContext * const context = data;
436
437     assert(context != NULL);
438
439     // Create the event loop.
440     GMainContext * const loop_context = g_main_context_new();
441     GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE);
442
443     g_main_context_push_thread_default(loop_context);
444
445     // Acquire the bus name after exporting our D-Bus objects.
446     guint const owner_id =
447         g_bus_own_name_on_connection(context->connection,
448                                      CA_DBUS_GATT_SERVICE_NAME,
449                                      G_BUS_NAME_OWNER_FLAGS_NONE,
450                                      CAPeripheralOnNameAcquired,
451                                      CAPeripheralOnNameLost,
452                                      NULL, // user_data,
453                                      NULL);
454
455     /**
456      * Create proxies to the @c org.bluez.LEAdvertisingManager1 D-Bus
457      * objects that will later be used to register the OIC LE
458      * advertisement data.
459      *
460      * @todo Failure to retrieve the LE advertising managers is
461      *       currently ignored.  We should propagate the failure to
462      *       the thread that spawned this one.
463      *
464      * @note Retrieval of the @c org.bluez.LEAdvertisingManager1
465      *       proxies must be done in a thread separate from the one
466      *       that makes calls through those proxies since the
467      *       underlying GDBusObjectManagerClient sets up signal
468      *       subscriptions that are used when dispatching D-Bus method
469      *       handling calls (e.g. property retrieval, etc).
470      *       Otherwise, a distributed deadlock situation could occur
471      *       if a synchronous D-Bus proxy call is made that causes the
472      *       recipient (like BlueZ) to call back in to the thread that
473      *       handles signals.  For example, registration of our LE
474      *       advertisment with BlueZ causes BlueZ itself to make a
475      *       call to our own @c org.bluez.LEAdvertisement1 object.
476      *       However, the thread that initiated the advertisement
477      *       registration is blocked waiting for BlueZ to respond, but
478      *       BlueZ is blocked waiting for that same thread to respond
479      *       to its own advertisement property retrieval call.
480      */
481     GList * advertising_managers = NULL;
482     if (!CAGetBlueZManagedObjectProxies(
483             &advertising_managers,
484             BLUEZ_ADVERTISING_MANAGER_INTERFACE,
485             context,
486             NULL))
487     {
488         OIC_LOG(ERROR,
489                 TAG,
490                 "Failed to retrieve BlueZ LE advertising "
491                 "manager interface.");
492     }
493
494     /**
495      * Initialize all GATT services.
496      *
497      * @todo Failure to initialize the OIC GATT services is currently
498      *       ignored.  We should propagate the failure to the thread
499      *       that spawned this one.
500      *
501      * @note See the @c org.bluez.LEAdvertisingManager1 note above to
502      *       understand why the GATT services must be initialized in
503      *       a thread seperate from the one that initiates GATT
504      *       service registration.
505      */
506     GList * const gatt_services =
507         CAPeripheralInitializeGattServices(context);
508
509     ca_mutex_lock(g_context.lock);
510
511     assert(g_context.event_loop == NULL);
512     g_context.event_loop = event_loop;
513
514     g_context.base = context;
515
516     g_context.owner_id = owner_id;
517
518     CALEAdvertisementInitialize(&g_context.advertisement,
519                                 context->connection,
520                                 advertising_managers);
521
522     g_context.gatt_services = gatt_services;
523
524     ca_mutex_unlock(g_context.lock);
525
526     ca_cond_signal(g_context.condition);
527
528     g_main_loop_run(event_loop);
529 }
530
531 static void CAPeripheralStopEventLoop(CAPeripheralContext * context)
532 {
533     ca_mutex_lock(context->lock);
534
535     GMainLoop * const event_loop = context->event_loop;
536     context->event_loop = NULL;
537
538     ca_mutex_unlock(context->lock);
539
540     if (event_loop != NULL)
541     {
542         g_main_loop_quit(event_loop);
543
544         GMainContext * const loop_context =
545             g_main_loop_get_context(event_loop);
546
547         if (loop_context != NULL)
548         {
549             g_main_context_wakeup(loop_context);
550             g_main_context_unref(loop_context);
551         }
552
553         g_main_loop_unref(event_loop);
554     }
555 }
556
557 // ------------------------------------------------------
558
559 void CAPeripheralInitialize()
560 {
561     g_context.lock      = ca_mutex_new();
562     g_context.condition = ca_cond_new();
563 }
564
565 void CAPeripheralFinalize()
566 {
567     ca_cond_free(g_context.condition);
568     ca_mutex_free(g_context.lock);
569 }
570
571 CAResult_t CAPeripheralStart(CALEContext * context)
572 {
573     /**
574      * @todo Bluetooth adapters that are hot-plugged after the
575      *       peripheral has started will not be started!
576      */
577
578     CAResult_t result = CA_STATUS_FAILED;
579
580     // Only start if we were previously stopped.
581     if (CAPeripheralCheckStarted())
582     {
583         result = CA_SERVER_STARTED_ALREADY;
584         return result;
585     }
586
587     if (!CAPeripheralAdaptersFound(context))
588     {
589         // No Bluetooth adapters.  Don't bother continuing.
590         return result;
591     }
592
593     /*
594       Spawn a thread to run the Glib event loop that will drive D-Bus
595       signal handling.
596      */
597     result = ca_thread_pool_add_task(context->server_thread_pool,
598                                      CAPeripheralStartEventLoop,
599                                      context);
600
601     if (result != CA_STATUS_OK)
602     {
603         return result;
604     }
605
606     /*
607       Wait until initialization completes before proceeding to
608       service and advertisement registration.
609     */
610
611     // Number of times to wait for initialization to complete.
612     static int const max_retries = 2;
613
614     static uint64_t const timeout =
615         2 * MICROSECS_PER_SEC;  // Microseconds
616
617     ca_mutex_lock(g_context.lock);
618
619     for (int i = 0;
620          g_context.gatt_services == NULL && i < max_retries;
621          ++i)
622     {
623         if (ca_cond_wait_for(g_context.condition,
624                              g_context.lock,
625                              timeout) == 0)
626         {
627             result = CA_STATUS_OK;
628         }
629     }
630
631     ca_mutex_unlock(g_context.lock);
632
633     if (result == CA_STATUS_FAILED)
634     {
635         return result;
636     }
637
638     /**
639      * First register the GATT services, then register the LE
640      * advertisments with BlueZ to make sure the service we're
641      * advertising actually exists.
642      */
643     if (result == CA_STATUS_OK
644         && !(CAPeripheralRegisterGattServices(context)
645              && CAPeripheralRegisterAdvertisements(&g_context)))
646     {
647         result = CA_STATUS_FAILED;
648     }
649
650     /*
651       Make the local bluetooth adapters discoverable over LE by
652       enabling LE, enabling advertising, and making the LE device
653       connectable.
654     */
655     result = CAPeripheralSetDiscoverability(context,
656                                             CAPeripheralMakeDiscoverable);
657
658     return result;
659 }
660
661 CAResult_t CAPeripheralStop()
662 {
663     CAResult_t result = CA_STATUS_FAILED;
664
665     // Only stop if we were previously started.
666     if (!CAPeripheralCheckStarted())
667     {
668         result = CA_STATUS_OK;
669         return result;
670     }
671
672     /*
673       Make the local bluetooth adapters undiscoverable.
674
675       This function also sets the base context to NULL.
676     */
677     result =
678         CAPeripheralSetDiscoverability(g_context.base,
679                                        CAPeripheralMakeUndiscoverable);
680
681     CAPeripheralStopEventLoop(&g_context);
682
683     ca_mutex_lock(g_context.lock);
684
685     guint const owner_id = g_context.owner_id;
686     g_context.owner_id = 0;
687
688     GList * const gatt_services = g_context.gatt_services;
689     g_context.gatt_services = NULL;
690
691     g_context.base = NULL;
692
693     ca_mutex_unlock(g_context.lock);
694
695     CALEAdvertisementDestroy(&g_context.advertisement);
696
697     g_list_free_full(gatt_services, CAPeripheralDestroyGattServices);
698
699     g_bus_unown_name(owner_id);
700
701     return result;
702 }