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 "caleinterface.h"
22 #include "peripheral.h"
27 #include "cagattservice.h"
28 #include "oic_malloc.h"
29 #include "oic_string.h"
33 #include <strings.h> // For strcasecmp().
37 #define MICROSECS_PER_SEC 1000000
40 static char const TAG[] = "BLE_INTERFACE";
43 The IoTivity adapter interface currently doesn't provide a means to
44 pass context down to the transport layer so rely on a file scope
47 static CALEContext g_context = {
51 // -----------------------------------------------------------------------
52 // Functions internal to this BLE adapter implementation.
53 // -----------------------------------------------------------------------
54 static bool CALESetUpBlueZObjects(CALEContext * context);
56 static bool CALECheckStarted()
58 ca_mutex_lock(g_context.lock);
61 (g_context.event_loop != NULL
62 && g_main_loop_is_running(g_context.event_loop));
64 ca_mutex_unlock(g_context.lock);
67 * @todo Fix potential TOCTOU race condition. A LE transport
68 * adapter could have been started or stopped between the
69 * mutex unlock and boolean check.
74 static void CALEDumpDBusSignalParameters(char const * sender_name,
75 char const * object_path,
76 char const * interface_name,
77 char const * signal_name,
78 GVariant * parameters)
86 gchar * const param_dump =
87 g_variant_print(parameters, TRUE);
94 "\tinterface_name: %s\n"
108 static void CALEOnInterfaceProxyPropertiesChanged(
109 GDBusObjectManagerClient * manager,
110 GDBusObjectProxy * object_proxy,
111 GDBusProxy * interface_proxy,
112 GVariant * changed_properties,
113 gchar const * const * invalidated_properties,
118 (void)invalidated_properties;
120 char const * const interface_name =
121 g_dbus_proxy_get_interface_name(interface_proxy);
123 bool const is_adapter_interface =
124 (strcmp(BLUEZ_ADAPTER_INTERFACE, interface_name) == 0);
126 if (!is_adapter_interface)
129 Only specific org.bluez.Adapter1 property changes are
137 "%s properties Changed on %s:\n",
139 g_dbus_proxy_get_object_path(interface_proxy));
141 CALEContext * const context = user_data;
144 gchar const * key = NULL;
145 GVariant * value = NULL;
148 * @todo Since we're only looking up one value here,
149 * i.e. "Powered", can't we just use
150 * g_variant_lookup_value() instead of this while() loop?
152 g_variant_iter_init(&iter, changed_properties);
153 while (g_variant_iter_next(&iter, "{&sv}", &key, &value))
155 if (strcmp(key, "Powered") == 0)
158 Report a change in the availability of the bluetooth
161 gboolean const powered = g_variant_get_boolean(value);
162 CAAdapterState_t const status =
163 (powered ? CA_ADAPTER_ENABLED : CA_ADAPTER_DISABLED);
166 * @todo Should we acquire the context lock here to
167 * prevent the @c CALEDeviceStateChangedCallback
168 * from being potentially yanked out from under us
169 * if the CA adapters are stopped/terminated as
170 * we're about to invoke this callback?
172 * @todo Unfortunately the CA LE interface defined in
173 * caleinterface.h assumes that only one BLE adapter
174 * will exist on a given host. However, this
175 * implementation can handle multiple BLE adapters.
176 * The CA LE interface should be updated so that it
177 * can handle multiple BLE adapters.
179 context->on_device_state_changed(status);
183 gchar * const s = g_variant_print(value, TRUE);
184 OIC_LOG_V(DEBUG, TAG, " %s -> %s", key, s);
188 g_variant_unref(value);
192 static void CALEHandleInterfaceAdded(GList ** proxy_list,
193 char const * interface,
194 GVariant * parameters)
197 * @note The @a parameters are of the form "(oa{sv})".
199 GDBusProxy * const proxy =
200 CAGetBlueZInterfaceProxy(parameters,
202 g_context.object_manager);
209 ca_mutex_lock(g_context.lock);
212 Add the object information to the list.
214 Note that we prepend instead of append in this case since it
215 is more efficient to do so for linked lists like the one used
218 *proxy_list = g_list_prepend(*proxy_list, proxy);
220 ca_mutex_unlock(g_context.lock);
223 * Let the thread that may be blocked waiting for Devices to be
224 * discovered know that at least one was found.
226 * @todo It doesn't feel good putting this @c org.bluez.Device1
227 * specific code here since this function is meant to be
228 * BlueZ interface neutral. Look into ways of moving this
231 if (strcmp(interface, BLUEZ_DEVICE_INTERFACE) == 0)
233 ca_cond_signal(g_context.condition);
237 static void CALEOnInterfacesAdded(GDBusConnection * connection,
238 char const * sender_name,
239 char const * object_path,
240 char const * interface_name,
241 char const * signal_name,
242 GVariant * parameters,
247 CALEDumpDBusSignalParameters(sender_name,
253 // The signal should always be InterfacesAdded.
254 assert(strcmp(signal_name, "InterfacesAdded") == 0);
256 // Handle addition of a new org.bluez.Adapter1 interface.
257 CALEHandleInterfaceAdded(&g_context.adapters,
258 BLUEZ_ADAPTER_INTERFACE,
261 // Handle addition of a new org.bluez.Device1 interface.
262 CALEHandleInterfaceAdded(&g_context.devices,
263 BLUEZ_DEVICE_INTERFACE,
267 static void CALEOnInterfacesRemoved(GDBusConnection * connection,
268 char const * sender_name,
269 char const * object_path,
270 char const * interface_name,
271 char const * signal_name,
272 GVariant * parameters,
277 CALEDumpDBusSignalParameters(sender_name,
283 // The signal should always be InterfacesRemoved.
284 assert(strcmp(signal_name, "InterfacesRemoved") == 0);
287 The object path is first tuple element, and the interface names
288 the second. Check if "org.bluez.Adapter1" exists in the
289 interface array. If it does, remove the corresponding
290 information from the adapter_infos list.
292 GVariant * const interfaces =
293 g_variant_get_child_value(parameters, 1);
295 GVariantIter * iter = NULL;
296 g_variant_get(interfaces, "as", &iter);
299 * Iterate over the array and remove all BlueZ interface proxies
300 * with a matching D-Bus object path from the corresponding list.
302 * @todo Determine whether we should optimize this nested loop.
303 * It may not be worthwhile to do so since the lists being
304 * iterated over should be very short.
306 for (GVariant * child = g_variant_iter_next_value(iter);
308 child = g_variant_iter_next_value(iter))
310 char const * interface = NULL;
311 g_variant_get(child, "&s", &interface);
313 GList ** list = NULL;
315 if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0)
317 list = &g_context.adapters;
319 else if (strcmp(interface, BLUEZ_DEVICE_INTERFACE) == 0)
321 list = &g_context.devices;
328 // The object path is the first tuple element.
329 gchar const * path = NULL;
330 g_variant_get_child(parameters, 0, "&o", &path);
332 ca_mutex_lock(g_context.lock);
334 for (GList * l = *list; l != NULL; l = g_list_next(l))
336 GDBusProxy * const proxy = G_DBUS_PROXY(l->data);
339 g_dbus_proxy_get_object_path(proxy)) == 0)
342 * @todo If a BlueZ Device was removed, update the
343 * characteristic map, accordingly.
347 g_object_unref(proxy);
349 *list = g_list_delete_link(*list, l);
355 ca_mutex_unlock(g_context.lock);
357 g_variant_unref(child);
362 g_variant_iter_free(iter);
366 static void CALESubscribeToSignals(CALEContext * context,
367 GDBusConnection * connection,
368 GDBusObjectManager * object_manager)
370 static char const om_interface[] =
371 "org.freedesktop.DBus.ObjectManager";
374 Subscribe to D-Bus signals that will allow us to detect changes
375 in BlueZ adapter and device properties.
377 guint const interfaces_added_sub_id =
378 g_dbus_connection_signal_subscribe(
385 G_DBUS_SIGNAL_FLAGS_NONE,
386 CALEOnInterfacesAdded,
390 guint const interfaces_removed_sub_id =
391 g_dbus_connection_signal_subscribe(
398 G_DBUS_SIGNAL_FLAGS_NONE,
399 CALEOnInterfacesRemoved,
403 g_signal_connect(object_manager,
404 "interface-proxy-properties-changed",
405 G_CALLBACK(CALEOnInterfaceProxyPropertiesChanged),
408 ca_mutex_lock(context->lock);
410 context->interfaces_added_sub_id = interfaces_added_sub_id;
411 context->interfaces_removed_sub_id = interfaces_removed_sub_id;
413 ca_mutex_unlock(context->lock);
416 static bool CALESetUpDBus(CALEContext * context)
418 assert(context != NULL);
420 bool success = false;
422 GError * error = NULL;
425 Set up connection to the D-Bus system bus, where the BlueZ
428 GDBusConnection * const connection =
429 g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
431 if (connection == NULL)
435 "Connection to D-Bus system bus failed: %s.",
443 // Create a proxy to the BlueZ D-Bus ObjectManager.
444 static char const object_manager_path[] = "/";
446 GDBusObjectManager * const object_manager =
447 g_dbus_object_manager_client_new_sync(
449 G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
452 NULL, // get_proxy_type_func
453 NULL, // get_proxy_type_user_data
454 NULL, // get_proxy_type_destroy_notify
458 if (object_manager == NULL)
462 "Unable to create D-Bus ObjectManager client: %s",
467 g_object_unref(connection);
472 CALESubscribeToSignals(context, connection, object_manager);
474 ca_mutex_lock(context->lock);
475 context->connection = connection;
476 context->object_manager = object_manager;
477 ca_mutex_unlock(context->lock);
479 success = CALESetUpBlueZObjects(context);
484 static void CALETearDownDBus(CALEContext * context)
486 assert(context != NULL);
489 Minimize the time we hold the global lock by only clearing the
490 global state, and pushing resource finalization outside the global
493 ca_mutex_lock(context->lock);
495 GDBusConnection * const connection = context->connection;
496 context->connection = NULL;
498 GDBusObjectManager * const object_manager = context->object_manager;
499 context->object_manager = NULL;
501 GList * const objects = context->objects;
502 context->objects = NULL;
504 GList * const adapters = context->adapters;
505 context->adapters = NULL;
507 GList * const devices = context->devices;
508 context->devices = NULL;
510 guint const interfaces_added = context->interfaces_added_sub_id;
511 guint const interfaces_removed = context->interfaces_removed_sub_id;
513 context->interfaces_added_sub_id = 0;
514 context->interfaces_removed_sub_id = 0;
516 ca_mutex_unlock(context->lock);
518 // Destroy the device proxies list.
519 g_list_free_full(devices, g_object_unref);
521 // Destroy the adapter proxies list.
522 g_list_free_full(adapters, g_object_unref);
524 // Destroy the list of objects obtained from the ObjectManager.
525 g_list_free_full(objects, g_object_unref);
527 // Destroy the ObjectManager proxy.
528 if (object_manager != NULL)
530 g_object_unref(object_manager);
533 // Tear down the D-Bus connection to the system bus.
534 if (connection != NULL)
536 g_dbus_connection_signal_unsubscribe(connection,
538 g_dbus_connection_signal_unsubscribe(connection,
540 g_object_unref(connection);
544 static bool CALEDeviceFilter(GDBusProxy * device)
546 bool accepted = false;
549 Filter out any devices that don't support the OIC Transport
552 GVariant * const prop =
553 g_dbus_proxy_get_cached_property(device, "UUIDs");
557 // No remote services available on the device.
562 char const ** const UUIDs = g_variant_get_strv(prop, &length);
565 It would have been nice to use g_strv_contains() here, but we
566 would need to run it twice: once for the uppercase form of the
567 UUID and once for for the lowercase form. Just run the loop
568 manually, and use strcasecmp() instead.
570 char const * const * const end = UUIDs + length;
571 for (char const * const * u = UUIDs; u != end; ++u)
573 if (strcasecmp(*u, CA_GATT_SERVICE_UUID) == 0)
581 g_variant_unref(prop);
586 static bool CALESetUpBlueZObjects(CALEContext * context)
588 bool success = false;
590 // Get the list of BlueZ D-Bus objects.
591 GList * const objects =
592 g_dbus_object_manager_get_objects(context->object_manager);
594 if (objects == NULL) {
597 "Unable to get objects from ObjectManager.");
602 ca_mutex_lock(context->lock);
603 context->objects = objects;
604 ca_mutex_unlock(context->lock);
607 Create a proxies to the org.bluez.Adapter1 D-Bus objects that
608 will later be used to obtain local bluetooth adapter properties,
609 as well as by the BLE central code to discover peripherals.
611 GList * adapters = NULL;
612 success = CAGetBlueZManagedObjectProxies(&adapters,
613 BLUEZ_ADAPTER_INTERFACE,
617 // An empty adapters list is NULL.
618 if (success && adapters != NULL)
620 ca_mutex_lock(context->lock);
621 context->adapters = adapters;
622 ca_mutex_unlock(context->lock);
626 Create a proxies to the org.bluez.Device1 D-Bus objects that
627 will later be used to establish connections.
629 GList * devices = NULL;
630 success = CAGetBlueZManagedObjectProxies(&devices,
631 BLUEZ_DEVICE_INTERFACE,
635 // An empty device list is NULL.
636 if (success && devices != NULL)
638 ca_mutex_lock(context->lock);
639 context->devices = devices;
640 ca_mutex_unlock(context->lock);
647 * Inform thread waiting for the event loop to start that the loop has
648 * started. This is done in the context of the event loop itself so
649 * that we can be certain that the event loop is indeed running.
651 static gboolean CALEEventLoopStarted(gpointer user_data)
653 sem_t * const sem = user_data;
655 (void) sem_post(sem);
657 return G_SOURCE_REMOVE;
660 static void CALEStartEventLoop(void * data)
662 CALEContext * const context = data;
664 assert(context != NULL);
666 // Create the event loop.
667 GMainContext * const loop_context = g_main_context_new();
668 GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE);
670 g_main_context_push_thread_default(loop_context);
673 We have to do the BlueZ object manager client initialization and
674 signal subscription here so that the corresponding asynchronous
675 signal handling occurs in the same thread as the one running the
678 if (CALESetUpDBus(&g_context))
680 ca_mutex_lock(context->lock);
682 assert(context->event_loop == NULL);
683 context->event_loop = event_loop;
685 ca_mutex_unlock(context->lock);
688 Add an idle handler that notifies a thread waiting for the
689 GLib event loop to run that the event loop is actually
690 running. We do this in the context of the event loop itself
691 to avoid race conditions.
693 GSource * const source = g_idle_source_new();
694 g_source_set_priority(source, G_PRIORITY_HIGH_IDLE);
695 g_source_set_callback(source,
696 CALEEventLoopStarted,
697 &context->le_started, // data
699 (void) g_source_attach(source, loop_context);
700 g_source_unref(source);
702 g_main_loop_run(event_loop); // Blocks until loop is quit.
704 CALETearDownDBus(&g_context);
708 Clean up in the same thread to avoid having to explicitly bump
709 the ref count to retain ownership.
711 g_main_context_unref(loop_context);
712 g_main_loop_unref(event_loop);
715 static void CALEStopEventLoop(CALEContext * context)
717 ca_mutex_lock(context->lock);
719 GMainLoop * const event_loop = context->event_loop;
720 context->event_loop = NULL;
722 ca_mutex_unlock(context->lock);
724 if (event_loop != NULL)
726 g_main_loop_quit(event_loop);
728 GMainContext * const loop_context =
729 g_main_loop_get_context(event_loop);
731 if (loop_context != NULL)
733 g_main_context_wakeup(loop_context);
739 * Wait for @a list to be non-empty.
741 * @param[in] list List that should not be empty.
742 * @param[in] retries Number of times to retry if the @a timeout is
744 * @param[in] timeout Timeout in microseconds to wait between retries.
746 static bool CALEWaitForNonEmptyList(GList * const * list,
750 bool success = false;
752 ca_mutex_lock(g_context.lock);
754 for (int i = 0; *list == NULL && i < retries; ++i)
756 if (ca_cond_wait_for(g_context.condition,
758 timeout) == CA_WAIT_SUCCESS)
761 Condition variable was signaled before the timeout was
768 ca_mutex_unlock(g_context.lock);
773 // -----------------------------------------------------------------------
775 CAResult_t CAInitializeLEAdapter(const ca_thread_pool_t threadPool)
777 #if !GLIB_CHECK_VERSION(2,36,0)
779 Initialize the GLib type system.
781 As of GLib 2.36, it is no longer necessary to explicitly call
790 CAResult_t CAStartLEAdapter()
793 This function is called by the connectivity abstraction when
794 CASelectNetwork(CA_ADAPTER_GATT_BTLE) is called by the user.
797 OIC_LOG(DEBUG, TAG, __func__);
799 CAResult_t result = CA_STATUS_FAILED;
801 // Only start if we were previously stopped.
802 if (CALECheckStarted())
808 * Spawn a thread to run the GLib event loop that will drive D-Bus
811 * @note Ideally this should be done in the @c CAInitializeLE()
812 * function so that we can detect local bluetooth adapter
813 * changes right away, without having to first start this LE
814 * adapter/transport via @cCASelectNetwork(). However, a
815 * limitation in the CA termination code that destroys the
816 * thread pool before the transport adapters prevents us
817 * from doing that without potentially triggering a
818 * @c pthread_join() call that blocks indefinitely due to
819 * this event loop not being stopped. See the comments in
820 * the @c CAGetLEInterfaceInformation() function below for
823 result = ca_thread_pool_add_task(g_context.client_thread_pool,
828 Wait for the GLib event loop to actually run before returning.
830 This addresses corner cases where operations are incorrectly
831 permitted to run in parallel before the event loop is run. For
832 example, the LE transport could have been stopped in a thread
833 parallel to the one starting the event loop start. In that case
834 the GLib event loop may never exit since the stop operation that
835 causes the event loop to exit occurred before event loop
836 started. That ultimately causes the CA layer termination to
837 block indefinitely on a pthread_join(). The solution is to only
838 return from the LE transport start operation once we know the
839 event loop is up and running.
841 struct timespec abs_timeout;
842 if (result == CA_STATUS_OK
843 && clock_gettime(CLOCK_REALTIME, &abs_timeout) == 0)
845 static time_t const relative_timeout = 2; // seconds
846 abs_timeout.tv_sec += relative_timeout;
848 int const wait_result =
849 sem_timedwait(&g_context.le_started, &abs_timeout);
851 if (wait_result == 0)
853 result = CA_STATUS_OK;
860 CAResult_t CAStopLEAdapter()
863 This function is called by the connectivity abstraction when
864 CAUnselectNetwork(CA_ADAPTER_GATT_BTLE) is called by the user.
867 OIC_LOG(DEBUG, TAG, "Stop Linux BLE adapter.");
869 // Only stop if we were previously started.
870 if (!CALECheckStarted())
872 return CA_STATUS_FAILED;
875 CALEStopEventLoop(&g_context);
881 CAResult_t CAGetLEAdapterState()
883 CAResult_t result = CA_ADAPTER_NOT_ENABLED;
885 ca_mutex_lock(g_context.lock);
887 for (GList * l = g_context.adapters; l != NULL; l = l->next)
889 GDBusProxy * const adapter = G_DBUS_PROXY(l->data);
890 GVariant * const prop =
891 g_dbus_proxy_get_cached_property(adapter, "Powered");
895 // This should never happen!
896 result = CA_STATUS_FAILED;
900 gboolean const powered = g_variant_get_boolean(prop);
901 g_variant_unref(prop);
905 result = CA_STATUS_OK;
909 No need to continue iterating since we have at least
910 one enabled Bluetooth adapter.
915 ca_mutex_unlock(g_context.lock);
920 CAResult_t CAInitializeLENetworkMonitor()
923 * @note "Network monitor" operations are started in the
924 * @c CAStartLEAdapter() function rather than this function
925 * due to glib/D-Bus signal handling threads related
928 * @see @c CAStartLEAdapter() for further details.
931 g_context.lock = ca_mutex_new();
932 g_context.condition = ca_cond_new();
934 static int const PSHARED = 0; // shared between threads
935 static unsigned int const VALUE = 0; // force sem_wait() to block
937 if (sem_init(&g_context.le_started, PSHARED, VALUE) != 0)
939 return CA_STATUS_FAILED;
943 The CA LE interface doesn't expose a CAInitializeLEGattServer()
944 function so perform initialization here.
946 CAPeripheralInitialize();
951 void CATerminateLENetworkMonitor()
954 * @note "Network monitor" operations are stopped in @c CALEStop()
955 * since they are started in @c CAStartLEAdapter() rather
956 * than @c CAInitializeLENetworkMonitor().
958 * @see @c CAStartLEAdapter() for further details.
962 Since the CA LE interface doesn't expose a
963 CAInitializeLEGattServer() function, finalize the LE server
964 (peripheral) here rather than in CATerminateLEGattServer() to
965 ensure finalization is correctly paired with initialization.
967 CAPeripheralFinalize();
969 (void) sem_destroy(&g_context.le_started);
971 ca_mutex_lock(g_context.lock);
973 g_context.on_device_state_changed = NULL;
974 g_context.on_server_received_data = NULL;
975 g_context.on_client_received_data = NULL;
976 g_context.client_thread_pool = NULL;
977 g_context.server_thread_pool = NULL;
978 g_context.on_client_error = NULL;
979 g_context.on_server_error = NULL;
981 ca_cond_free(g_context.condition);
982 g_context.condition = NULL;
984 ca_mutex_unlock(g_context.lock);
986 ca_mutex_free(g_context.lock);
987 g_context.lock = NULL;
990 CAResult_t CASetLEAdapterStateChangedCb(
991 CALEDeviceStateChangedCallback callback)
993 ca_mutex_lock(g_context.lock);
994 g_context.on_device_state_changed = callback;
995 ca_mutex_unlock(g_context.lock);
1000 CAResult_t CASetLENWConnectionStateChangedCb(CALEConnectionStateChangedCallback callback)
1003 return CA_STATUS_OK;
1006 CAResult_t CAGetLEAddress(char **local_address)
1008 OIC_LOG(DEBUG, TAG, "Get Linux BLE local device information.");
1010 if (local_address == NULL)
1012 return CA_STATUS_INVALID_PARAM;
1016 * @bug Attempting to get LE interface information before this
1017 * connectivity abstraction adapter has started (e.g. via
1018 * @c CASelectNetwork()) could result in an inaccurate list
1019 * of LE interfaces. For example, detection of hot-plugged
1020 * bluetooth adapters will only work after the LE IoTivity
1021 * network has been selected. If the LE IoTivity network
1022 * hasn't been selected, the hot-plugged bluetooth adapter
1023 * will not be reflected in the @c CALocalConnectivity_t list
1024 * returned by this function.
1026 * This issue caused by the fact that the event loop that
1027 * handles such events can only be run after this LE
1028 * adapter/transport is started. The event loop cannot be
1029 * started earlier, e.g. during @c CAInitialize(), due to a
1030 * limitation in the CA transport termination code that
1031 * requires thread pools to be destroyed before transport
1032 * termination. If the event loop was added as a task to the
1033 * thread pool during @c CAInitialize(), it's possible that
1034 * the thread running that event loop could be blocked
1035 * waiting for events during a later call to
1036 * @c CATerminate() if @c CASelectNetwork() was not called
1037 * beforehand. The thread pool destruction that occurs
1038 * during @c CATerminate() waits for threads in the pool to
1039 * exit. However, in this case the event loop thread is
1040 * still blocked waiting for events since it may not have
1041 * been stopped since the transport itself was not stopped,
1042 * meaning termination will also be blocked.
1044 * Other than refactoring to the termination code to allow
1045 * thread pools to be started during @c CAInitialize(), the
1046 * only other choice we have to prevent the hang at
1047 * termination is to move the event loop creation and
1048 * termination to the @c CAAdapterStart() and
1049 * @c CAAdapterStop() implementations, respectively, which is
1050 * why we have this bug.
1052 if (!CALECheckStarted())
1053 return CA_ADAPTER_NOT_ENABLED;
1055 *local_address = NULL;
1057 ca_mutex_lock(g_context.lock);
1059 for (GList * l = g_context.adapters; l != NULL; l = l->next)
1061 GDBusProxy * const adapter = G_DBUS_PROXY(l->data);
1064 The local bluetooth adapter MAC address is stored in the
1065 org.bluez.Adapter1.Address property.
1067 GVariant * const prop =
1068 g_dbus_proxy_get_cached_property(adapter, "Address");
1071 Unless the org.bluez.Adapter1.Address property no longer
1072 exists, prop should not be NULL! We have bigger problems if
1073 this assert() is ever tripped since that would mean the
1074 org.bluez.Adapter1 D-Bus interface changed.
1076 assert(prop != NULL);
1078 gchar const * const address = g_variant_get_string(prop, NULL);
1080 *local_address = OICStrdup(address);
1083 No longer need the property variant. The address has been
1086 g_variant_unref(prop);
1089 * @todo Unfortunately the CA LE interface defined in
1090 * caleinterface.h assumes that only one BLE adapter
1091 * will exist on a given host. However, this
1092 * implementation can handle multiple BLE adapters. The
1093 * CA LE interface should be updated so that it can
1094 * handle multiple BLE adapters. For now we'll just
1095 * return the address for the first BLE adapter in the
1101 ca_mutex_unlock(g_context.lock);
1103 return *local_address != NULL ? CA_STATUS_OK : CA_STATUS_FAILED;
1106 CAResult_t CAStartLEGattServer()
1108 return CAPeripheralStart(&g_context);
1111 CAResult_t CAStopLEGattServer()
1113 return CAPeripheralStop();
1116 CAResult_t CAInitializeLEGattServer()
1118 return CA_STATUS_OK;
1121 void CATerminateLEGattServer()
1124 See CATerminateLENetworkMonitor() to understand why the LE
1125 peripheral is not finalized here.
1129 void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback)
1131 ca_mutex_lock(g_context.lock);
1132 g_context.on_server_received_data = callback;
1133 ca_mutex_unlock(g_context.lock);
1136 CAResult_t CAUpdateCharacteristicsToGattClient(char const * address,
1137 uint8_t const * value,
1140 return CAGattServerSendResponseNotification(address,
1145 CAResult_t CAUpdateCharacteristicsToAllGattClients(uint8_t const * value,
1148 return CAGattServerSendResponseNotificationToAll(value, valueLen);
1151 CAResult_t CAStartLEGattClient()
1153 CAResult_t result = CACentralStart(&g_context);
1155 if (result != CA_STATUS_OK)
1160 ca_mutex_lock(g_context.lock);
1161 bool found_peripherals = (g_context.devices != NULL);
1162 ca_mutex_unlock(g_context.lock);
1164 if (!found_peripherals)
1166 // Wait for LE peripherals to be discovered.
1168 // Number of times to wait for discovery to complete.
1169 static int const retries = 5;
1171 static uint64_t const timeout =
1172 2 * MICROSECS_PER_SEC; // Microseconds
1174 if (!CALEWaitForNonEmptyList(&g_context.devices,
1183 Stop discovery so that we can connect to LE peripherals.
1184 Otherwise, the bluetooth subsystem will claim the adapter is
1188 result = CACentralStopDiscovery(&g_context);
1190 if (result != CA_STATUS_OK)
1195 bool const connected = CACentralConnectToAll(&g_context);
1203 * @todo Verify notifications have been enabled on all response
1207 return CAGattClientInitialize(&g_context);
1210 void CAStopLEGattClient()
1212 CAGattClientDestroy();
1213 (void) CACentralStop(&g_context);
1216 CAResult_t CAInitializeLEGattClient()
1218 return CA_STATUS_OK;
1221 void CATerminateLEGattClient()
1225 CAResult_t CAUpdateCharacteristicsToGattServer(
1226 char const * remoteAddress,
1227 uint8_t const * data,
1229 CALETransferType_t type,
1234 if (type != LE_UNICAST)
1236 return CA_STATUS_INVALID_PARAM;
1240 We can assume that we're already connected to the BLE device
1241 with the given remote address - we wouldn't have a remote
1242 address otherwise - so there is no need to start scanning for
1246 return CAGattClientSendData(remoteAddress,
1252 CAResult_t CAUpdateCharacteristicsToAllGattServers(uint8_t const * data,
1256 Now send the request through all BLE connections through the
1257 corresponding OIC GATT request characterstics.
1259 return CAGattClientSendDataToAll(data, length, &g_context);
1262 * @todo Should we restart discovery after the send?
1266 void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback)
1268 ca_mutex_lock(g_context.lock);
1269 g_context.on_client_received_data = callback;
1270 ca_mutex_unlock(g_context.lock);
1273 void CASetLEServerThreadPoolHandle(ca_thread_pool_t handle)
1275 ca_mutex_lock(g_context.lock);
1276 g_context.server_thread_pool = handle;
1277 ca_mutex_unlock(g_context.lock);
1280 void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle)
1282 ca_mutex_lock(g_context.lock);
1283 g_context.client_thread_pool = handle;
1284 ca_mutex_unlock(g_context.lock);
1287 CAResult_t CAUnSetLEAdapterStateChangedCb()
1289 ca_mutex_lock(g_context.lock);
1290 g_context.on_device_state_changed = NULL;
1291 ca_mutex_unlock(g_context.lock);
1293 return CA_STATUS_OK;
1296 void CASetBLEClientErrorHandleCallback(CABLEErrorHandleCallback callback)
1298 ca_mutex_lock(g_context.lock);
1299 g_context.on_client_error = callback;
1300 ca_mutex_unlock(g_context.lock);
1303 void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback)
1305 ca_mutex_lock(g_context.lock);
1306 g_context.on_server_error = callback;
1307 ca_mutex_unlock(g_context.lock);