1 /* ****************************************************************
3 * Copyright 2015 Intel Corporation All Rights Reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 ******************************************************************/
19 #include "peripheral.h"
24 #include "oic_malloc.h"
31 #define MICROSECS_PER_SEC 1000000
34 static char const TAG[] = "BLE_PERIPHERAL";
36 static CAPeripheralContext g_context = {
40 static bool CAPeripheralCheckStarted()
42 oc_mutex_lock(g_context.lock);
45 (g_context.event_loop != NULL
46 && g_main_loop_is_running(g_context.event_loop));
48 oc_mutex_unlock(g_context.lock);
51 * @todo Fix potential TOCTOU race condition. A peripheral could
52 * have been started or stopped between the mutex unlock and
59 static bool CAPeripheralAdaptersFound(CALEContext * context)
61 // Check if BlueZ detected bluetooth hardware adapters.
62 oc_mutex_lock(context->lock);
64 bool const found = (context->adapters != NULL);
66 oc_mutex_unlock(context->lock);
70 OIC_LOG(WARNING, TAG, "No bluetooth hardware found.");
76 static void CAPeripheralDestroyGattServices(gpointer data)
78 CAGattService * const service = data;
80 CAGattServiceDestroy(service);
85 static GList * CAPeripheralInitializeGattServices(CALEContext * context)
87 GList * result = NULL;
90 Create a proxies to the org.bluez.GattManager1 D-Bus objects that
91 will later be used to register the OIC GATT service.
93 GList * gatt_managers = NULL;
94 if (!CAGetBlueZManagedObjectProxies(&gatt_managers,
95 BLUEZ_GATT_MANAGER_INTERFACE,
100 GList * gatt_services = NULL;
102 for (GList * l = gatt_managers; l != NULL; )
104 CAGattService * const service = OICCalloc(1, sizeof(*service));
108 g_list_free_full(gatt_services,
109 CAPeripheralDestroyGattServices);
110 g_list_free_full(gatt_managers, g_object_unref);
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.
119 GDBusProxy * const manager = G_DBUS_PROXY(l->data);
120 char const * const path = g_dbus_proxy_get_object_path(manager);
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.
127 char const * const hci_name = strrchr(path, '/');
130 || !CAGattServiceInitialize(service, hci_name + 1, context))
132 g_list_free_full(gatt_services,
133 CAPeripheralDestroyGattServices);
134 g_list_free_full(gatt_managers, g_object_unref);
138 service->gatt_manager = manager;
141 The GattManager1 proxy is now owned by the CAGattService
145 GList * const tmp = l;
147 gatt_managers = g_list_delete_link(gatt_managers, tmp);
149 // Prepend for efficiency.
150 gatt_services = g_list_prepend(gatt_services, service);
153 result = gatt_services;
158 static bool CAPeripheralRegisterGattServices(
159 CAPeripheralContext * context)
161 assert(context != NULL);
165 oc_mutex_lock(context->lock);
167 for (GList * l = context->gatt_services; l != NULL; l = l->next)
169 CAGattService * const service = l->data;
171 // Register the OIC service with the corresponding BlueZ Gatt
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.
180 GVariant * const parameters =
181 g_variant_new("(oa{sv})", service->object_path, NULL);
183 GError * error = NULL;
185 GVariant * const ret =
186 g_dbus_proxy_call_sync(
187 service->gatt_manager,
190 G_DBUS_CALL_FLAGS_NONE,
191 -1, // timeout (default == -1),
199 "GATT service registration failed: %s",
209 g_variant_unref(ret);
212 oc_mutex_unlock(context->lock);
217 static bool CAPeripheralRegisterAdvertisements(
218 CAPeripheralContext * context)
220 bool success = false;
223 Register the OIC LE advertisement service with each BlueZ
224 LE Advertisement Manager.
227 oc_mutex_lock(context->lock);
229 char const * const advertisement_path =
230 g_dbus_interface_skeleton_get_object_path(
231 G_DBUS_INTERFACE_SKELETON(
232 context->advertisement.advertisement));
234 GList * managers = context->advertisement.managers;
236 for (GList * l = managers; l != NULL; )
238 GDBusProxy * const manager = G_DBUS_PROXY(l->data);
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.
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
253 GVariant * const parameters =
254 g_variant_new("(oa{sv})", advertisement_path, NULL);
256 GError * error = NULL;
258 GVariant * const ret =
259 g_dbus_proxy_call_sync(
261 "RegisterAdvertisement",
263 G_DBUS_CALL_FLAGS_NONE,
264 -1, // timeout (default == -1),
272 "LE advertisement registration on %s failed: %s",
273 g_dbus_proxy_get_object_path(manager),
278 // We can't use the LE advertising manager. Drop it.
279 g_object_unref(manager);
281 GList * const tmp = l;
283 managers = g_list_delete_link(managers, tmp);
288 g_variant_unref(ret);
291 Note that we can do this in the for-statement because of
292 the similar code in the error case above.
297 // Use the updated list of managers.
298 context->advertisement.managers = managers;
300 if (managers == NULL) // Empty
304 "LE advertisment registration failed for all "
305 "Bluetooth adapters.");
313 oc_mutex_unlock(context->lock);
318 static void CAPeripheralSetDiscoverable(gpointer data,
320 gboolean discoverable)
322 assert(data != NULL);
323 assert(user_data != NULL);
325 GDBusProxy * const adapter = G_DBUS_PROXY(data);
326 CAResult_t * const result = (CAResult_t *) user_data;
327 *result = CA_STATUS_FAILED;
330 Make sure the adapter is powered on before making it
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.
338 && !CASetBlueZObjectProperty(adapter,
339 BLUEZ_ADAPTER_INTERFACE,
341 g_variant_new_boolean(discoverable)))
345 "Unable to power %s LE peripheral adapter.",
346 discoverable ? "on" : "off");
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.
361 *result = CA_STATUS_OK;
364 static void CAPeripheralMakeDiscoverable(gpointer adapter,
367 CAPeripheralSetDiscoverable(adapter, result, TRUE);
370 static void CAPeripheralMakeUndiscoverable(gpointer adapter,
373 CAPeripheralSetDiscoverable(adapter, result, FALSE);
376 static CAResult_t CAPeripheralSetDiscoverability(
377 CALEContext * context,
378 GFunc discoverability_func)
380 CAResult_t result = CA_STATUS_FAILED;
383 Synchronize access to the adapter information using the base
384 context lock since we don't own the adapter_infos.
386 oc_mutex_lock(context->lock);
388 // Make all detected adapters discoverable.
389 g_list_foreach(context->adapters,
390 discoverability_func,
393 oc_mutex_unlock(context->lock);
399 * Callback function invoked when a D-Bus bus name is acquired.
401 * @param[in] connection The D-Bus connection on which the name was
403 * @param[in] name The bus name that was acquired.
404 * @param[in] user_data User-provided data.
406 static void CAPeripheralOnNameAcquired(GDBusConnection * connection,
411 (void)name; // needed when logging is a noop
415 "Name \"%s\" acquired on D-Bus.", name);
419 * Callback function invoked when a D-Bus bus name is no longer owned
420 * or the D-Bus connection has been closed.
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.
427 static void CAPeripheralOnNameLost(GDBusConnection * connection,
432 (void)name; // needed when logging is a noop
435 This can happen if the appropriate D-Bus policy is not
436 installed, for example.
440 "Lost name \"%s\" on D-Bus!", name);
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.
448 static gboolean CAPeripheralEventLoopStarted(gpointer user_data)
450 oc_cond const condition = user_data;
452 oc_cond_signal(condition); // For service registration
454 return G_SOURCE_REMOVE;
457 static void CAPeripheralStartEventLoop(void * data)
459 CALEContext * const context = data;
461 assert(context != NULL);
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);
467 g_main_context_push_thread_default(loop_context);
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,
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.
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.
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.
505 GList * advertising_managers = NULL;
506 if (!CAGetBlueZManagedObjectProxies(
507 &advertising_managers,
508 BLUEZ_ADVERTISING_MANAGER_INTERFACE,
514 "Failed to retrieve BlueZ LE advertising "
515 "manager interface.");
518 oc_mutex_lock(g_context.lock);
520 assert(g_context.event_loop == NULL);
521 g_context.event_loop = event_loop;
523 g_context.base = context;
525 g_context.owner_id = owner_id;
528 * Initialize all GATT services.
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.
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.
539 g_context.gatt_services = CAPeripheralInitializeGattServices(context);
541 CALEAdvertisementInitialize(&g_context.advertisement,
543 advertising_managers);
545 oc_mutex_unlock(g_context.lock);
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.
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
559 (void) g_source_attach(source, loop_context);
560 g_source_unref(source);
562 g_main_loop_run(event_loop); // Blocks until loop is quit.
565 Clean up in the same thread to avoid having to explicitly bump
566 the ref count to retain ownership.
568 g_main_context_unref(loop_context);
569 g_main_loop_unref(event_loop);
572 static void CAPeripheralStopEventLoop(CAPeripheralContext * context)
574 oc_mutex_lock(context->lock);
576 GMainLoop * const event_loop = context->event_loop;
577 context->event_loop = NULL;
579 oc_mutex_unlock(context->lock);
581 if (event_loop != NULL)
583 g_main_loop_quit(event_loop);
585 GMainContext * const loop_context =
586 g_main_loop_get_context(event_loop);
588 if (loop_context != NULL)
590 g_main_context_wakeup(loop_context);
595 // ------------------------------------------------------
597 void CAPeripheralInitialize()
599 g_context.lock = oc_mutex_new();
600 g_context.condition = oc_cond_new();
603 void CAPeripheralFinalize()
605 oc_cond_free(g_context.condition);
606 oc_mutex_free(g_context.lock);
609 CAResult_t CAPeripheralStart(CALEContext * context)
612 * @todo Bluetooth adapters that are hot-plugged after the
613 * peripheral has started will not be started!
616 CAResult_t result = CA_STATUS_FAILED;
618 // Only start if we were previously stopped.
619 if (CAPeripheralCheckStarted())
621 result = CA_SERVER_STARTED_ALREADY;
625 if (!CAPeripheralAdaptersFound(context))
627 // No Bluetooth adapters. Don't bother continuing.
632 Spawn a thread to run the Glib event loop that will drive D-Bus
635 result = ca_thread_pool_add_task(context->server_thread_pool,
636 CAPeripheralStartEventLoop,
639 if (result != CA_STATUS_OK)
645 Wait until initialization completes and the event loop is up and
646 running before proceeding to service and advertisement
650 // Number of times to wait for initialization to complete.
651 static int const max_retries = 2;
653 static uint64_t const timeout =
654 2 * MICROSECS_PER_SEC; // Microseconds
656 oc_mutex_lock(g_context.lock);
659 g_context.gatt_services == NULL && i < max_retries;
662 if (oc_cond_wait_for(g_context.condition,
664 timeout) == OC_WAIT_SUCCESS)
666 result = CA_STATUS_OK;
670 oc_mutex_unlock(g_context.lock);
672 if (result != CA_STATUS_OK)
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.
682 if (result == CA_STATUS_OK
683 && !(CAPeripheralRegisterGattServices(&g_context)
684 && CAPeripheralRegisterAdvertisements(&g_context)))
686 result = CA_STATUS_FAILED;
690 Make the local bluetooth adapters discoverable over LE by
691 enabling LE, enabling advertising, and making the LE device
694 result = CAPeripheralSetDiscoverability(context,
695 CAPeripheralMakeDiscoverable);
700 CAResult_t CAPeripheralStop()
702 CAResult_t result = CA_STATUS_FAILED;
704 // Only stop if we were previously started.
705 if (!CAPeripheralCheckStarted())
711 Make the local bluetooth adapters undiscoverable.
713 This function also sets the base context to NULL.
716 CAPeripheralSetDiscoverability(g_context.base,
717 CAPeripheralMakeUndiscoverable);
719 CAPeripheralStopEventLoop(&g_context);
721 oc_mutex_lock(g_context.lock);
723 guint const owner_id = g_context.owner_id;
724 g_context.owner_id = 0;
726 GList * const gatt_services = g_context.gatt_services;
727 g_context.gatt_services = NULL;
729 g_context.base = NULL;
731 oc_mutex_unlock(g_context.lock);
733 CALEAdvertisementDestroy(&g_context.advertisement);
735 g_list_free_full(gatt_services, CAPeripheralDestroyGattServices);
737 g_bus_unown_name(owner_id);
742 void CAPeripheralForEachService(GFunc func, void * user_data)
744 oc_mutex_lock(g_context.lock);
746 g_list_foreach(g_context.gatt_services, func, user_data);
748 oc_mutex_unlock(g_context.lock);