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"
23 #include "characteristic.h"
24 #include "descriptor.h"
26 #include "oic_malloc.h"
33 #define MICROSECS_PER_SEC 1000000
36 static char const TAG[] = "BLE_PERIPHERAL";
38 static CAPeripheralContext g_context = {
42 static bool CAPeripheralCheckStarted()
44 ca_mutex_lock(g_context.lock);
46 bool const started = (g_context.base != NULL);
48 ca_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 ca_mutex_lock(context->lock);
64 bool const found = (context->adapters != NULL);
66 ca_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, context, hci_name + 1))
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 proxies are now owned by the CAGattService
144 GList * const tmp = l;
146 gatt_managers = g_list_delete_link(gatt_managers, tmp);
148 // Prepend for efficiency.
149 gatt_services = g_list_prepend(gatt_services, service);
152 result = gatt_services;
157 static bool CAPeripheralRegisterGattServices(
158 CAPeripheralContext * context)
160 assert(context != NULL);
162 bool success = false;
164 ca_mutex_lock(context->lock);
166 for (GList * l = context->gatt_services; l != NULL; l = l->next)
168 CAGattService * const service = l->data;
170 // Register the OIC service with the corresponding BlueZ Gatt
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.
179 GVariant * const parameters =
180 g_variant_new("(oa{sv})", service->object_path, NULL);
182 GError * error = NULL;
184 GVariant * const ret =
185 g_dbus_proxy_call_sync(
186 service->gatt_manager,
189 G_DBUS_CALL_FLAGS_NONE,
190 -1, // timeout (default == -1),
198 "GATT service registration failed: %s",
208 g_variant_unref(ret);
211 ca_mutex_unlock(context->lock);
218 static bool CAPeripheralRegisterAdvertisements(
219 CAPeripheralContext * context)
221 bool success = false;
224 Register the OIC LE advertisement service with each BlueZ
225 LE Advertisement Manager.
228 ca_mutex_lock(context->lock);
230 char const * const advertisement_path =
231 g_dbus_interface_skeleton_get_object_path(
232 G_DBUS_INTERFACE_SKELETON(
233 context->advertisement.advertisement));
235 GList * managers = context->advertisement.managers;
237 for (GList * l = managers; l != NULL; )
239 GDBusProxy * const manager = G_DBUS_PROXY(l->data);
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.
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
254 GVariant * const parameters =
255 g_variant_new("(oa{sv})", advertisement_path, NULL);
257 GError * error = NULL;
259 GVariant * const ret =
260 g_dbus_proxy_call_sync(
262 "RegisterAdvertisement",
264 G_DBUS_CALL_FLAGS_NONE,
265 -1, // timeout (default == -1),
273 "LE advertisement registration on %s failed: %s",
274 g_dbus_proxy_get_object_path(manager),
279 // We can't use the LE advertising manager. Drop it.
280 g_object_unref(manager);
282 GList * const tmp = l;
284 managers = g_list_delete_link(managers, tmp);
289 g_variant_unref(ret);
292 Note that we can do this in the for-statement because of
293 the similar code in the error case above.
298 // Use the updated list of managers.
299 context->advertisement.managers = managers;
301 if (managers == NULL) // Empty
305 "LE advertisment registration failed for all "
306 "Bluetooth adapters.");
314 ca_mutex_unlock(context->lock);
319 static void CAPeripheralSetDiscoverable(gpointer data,
321 gboolean discoverable)
323 assert(data != NULL);
324 assert(user_data != NULL);
326 GDBusProxy * const adapter = G_DBUS_PROXY(data);
327 CAResult_t * const result = (CAResult_t *) user_data;
328 *result = CA_STATUS_FAILED;
331 Make sure the adapter is powered on before making it
334 if (!CASetBlueZObjectProperty(adapter,
335 BLUEZ_ADAPTER_INTERFACE,
337 g_variant_new_boolean(discoverable)))
341 "Unable to power %s LE peripheral adapter.",
342 discoverable ? "on" : "off");
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.
357 *result = CA_STATUS_OK;
360 static void CAPeripheralMakeDiscoverable(gpointer adapter,
363 CAPeripheralSetDiscoverable(adapter, result, TRUE);
366 static void CAPeripheralMakeUndiscoverable(gpointer adapter,
369 CAPeripheralSetDiscoverable(adapter, result, FALSE);
372 static CAResult_t CAPeripheralSetDiscoverability(
373 CALEContext * context,
374 GFunc discoverability_func)
376 CAResult_t result = CA_STATUS_FAILED;
379 Synchronize access to the adapter information using the base
380 context lock since we don't own the adapter_infos.
382 ca_mutex_lock(context->lock);
384 // Make all detected adapters discoverable.
385 g_list_foreach(context->adapters,
386 discoverability_func,
389 ca_mutex_unlock(context->lock);
395 * Callback function invoked when a D-Bus bus name is acquired.
397 * @param[in] connection The D-Bus connection on which the name was
399 * @param[in] name The bus name that was acquired.
400 * @param[in] user_data User-provided data.
402 static void CAPeripheralOnNameAcquired(GDBusConnection * connection,
408 "Name \"%s\" acquired on D-Bus.", name);
412 * Callback function invoked when a D-Bus bus name is no longer owned
413 * or the D-Bus connection has been closed.
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.
420 static void CAPeripheralOnNameLost(GDBusConnection * connection,
425 This can happen if the appropriate D-Bus policy is not
426 installed, for example.
430 "Lost name \"%s\" on D-Bus!", name);
433 static void CAPeripheralStartEventLoop(void * data)
435 CALEContext * const context = data;
437 assert(context != NULL);
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);
443 g_main_context_push_thread_default(loop_context);
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,
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.
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.
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.
481 GList * advertising_managers = NULL;
482 if (!CAGetBlueZManagedObjectProxies(
483 &advertising_managers,
484 BLUEZ_ADVERTISING_MANAGER_INTERFACE,
490 "Failed to retrieve BlueZ LE advertising "
491 "manager interface.");
495 * Initialize all GATT services.
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.
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.
506 GList * const gatt_services =
507 CAPeripheralInitializeGattServices(context);
509 ca_mutex_lock(g_context.lock);
511 assert(g_context.event_loop == NULL);
512 g_context.event_loop = event_loop;
514 g_context.base = context;
516 g_context.owner_id = owner_id;
518 CALEAdvertisementInitialize(&g_context.advertisement,
520 advertising_managers);
522 g_context.gatt_services = gatt_services;
524 ca_mutex_unlock(g_context.lock);
526 ca_cond_signal(g_context.condition);
528 g_main_loop_run(event_loop);
531 static void CAPeripheralStopEventLoop(CAPeripheralContext * context)
533 ca_mutex_lock(context->lock);
535 GMainLoop * const event_loop = context->event_loop;
536 context->event_loop = NULL;
538 ca_mutex_unlock(context->lock);
540 if (event_loop != NULL)
542 g_main_loop_quit(event_loop);
544 GMainContext * const loop_context =
545 g_main_loop_get_context(event_loop);
547 if (loop_context != NULL)
549 g_main_context_wakeup(loop_context);
550 g_main_context_unref(loop_context);
553 g_main_loop_unref(event_loop);
557 // ------------------------------------------------------
559 void CAPeripheralInitialize()
561 g_context.lock = ca_mutex_new();
562 g_context.condition = ca_cond_new();
565 void CAPeripheralFinalize()
567 ca_cond_free(g_context.condition);
568 ca_mutex_free(g_context.lock);
571 CAResult_t CAPeripheralStart(CALEContext * context)
574 * @todo Bluetooth adapters that are hot-plugged after the
575 * peripheral has started will not be started!
578 CAResult_t result = CA_STATUS_FAILED;
580 // Only start if we were previously stopped.
581 if (CAPeripheralCheckStarted())
583 result = CA_SERVER_STARTED_ALREADY;
587 if (!CAPeripheralAdaptersFound(context))
589 // No Bluetooth adapters. Don't bother continuing.
594 Spawn a thread to run the Glib event loop that will drive D-Bus
597 result = ca_thread_pool_add_task(context->server_thread_pool,
598 CAPeripheralStartEventLoop,
601 if (result != CA_STATUS_OK)
607 Wait until initialization completes before proceeding to
608 service and advertisement registration.
611 // Number of times to wait for initialization to complete.
612 static int const max_retries = 2;
614 static uint64_t const timeout =
615 2 * MICROSECS_PER_SEC; // Microseconds
617 ca_mutex_lock(g_context.lock);
620 g_context.gatt_services == NULL && i < max_retries;
623 if (ca_cond_wait_for(g_context.condition,
627 result = CA_STATUS_OK;
631 ca_mutex_unlock(g_context.lock);
633 if (result == CA_STATUS_FAILED)
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.
643 if (result == CA_STATUS_OK
644 && !(CAPeripheralRegisterGattServices(context)
645 && CAPeripheralRegisterAdvertisements(&g_context)))
647 result = CA_STATUS_FAILED;
651 Make the local bluetooth adapters discoverable over LE by
652 enabling LE, enabling advertising, and making the LE device
655 result = CAPeripheralSetDiscoverability(context,
656 CAPeripheralMakeDiscoverable);
661 CAResult_t CAPeripheralStop()
663 CAResult_t result = CA_STATUS_FAILED;
665 // Only stop if we were previously started.
666 if (!CAPeripheralCheckStarted())
668 result = CA_STATUS_OK;
673 Make the local bluetooth adapters undiscoverable.
675 This function also sets the base context to NULL.
678 CAPeripheralSetDiscoverability(g_context.base,
679 CAPeripheralMakeUndiscoverable);
681 CAPeripheralStopEventLoop(&g_context);
683 ca_mutex_lock(g_context.lock);
685 guint const owner_id = g_context.owner_id;
686 g_context.owner_id = 0;
688 GList * const gatt_services = g_context.gatt_services;
689 g_context.gatt_services = NULL;
691 g_context.base = NULL;
693 ca_mutex_unlock(g_context.lock);
695 CALEAdvertisementDestroy(&g_context.advertisement);
697 g_list_free_full(gatt_services, CAPeripheralDestroyGattServices);
699 g_bus_unown_name(owner_id);