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)
778 #if !GLIB_CHECK_VERSION(2,36,0)
780 Initialize the GLib type system.
782 As of GLib 2.36, it is no longer necessary to explicitly call
791 CAResult_t CAStartLEAdapter()
794 This function is called by the connectivity abstraction when
795 CASelectNetwork(CA_ADAPTER_GATT_BTLE) is called by the user.
798 OIC_LOG(DEBUG, TAG, __func__);
800 CAResult_t result = CA_STATUS_FAILED;
802 // Only start if we were previously stopped.
803 if (CALECheckStarted())
809 * Spawn a thread to run the GLib event loop that will drive D-Bus
812 * @note Ideally this should be done in the @c CAInitializeLE()
813 * function so that we can detect local bluetooth adapter
814 * changes right away, without having to first start this LE
815 * adapter/transport via @cCASelectNetwork(). However, a
816 * limitation in the CA termination code that destroys the
817 * thread pool before the transport adapters prevents us
818 * from doing that without potentially triggering a
819 * @c pthread_join() call that blocks indefinitely due to
820 * this event loop not being stopped. See the comments in
821 * the @c CAGetLEInterfaceInformation() function below for
824 result = ca_thread_pool_add_task(g_context.client_thread_pool,
829 Wait for the GLib event loop to actually run before returning.
831 This addresses corner cases where operations are incorrectly
832 permitted to run in parallel before the event loop is run. For
833 example, the LE transport could have been stopped in a thread
834 parallel to the one starting the event loop start. In that case
835 the GLib event loop may never exit since the stop operation that
836 causes the event loop to exit occurred before event loop
837 started. That ultimately causes the CA layer termination to
838 block indefinitely on a pthread_join(). The solution is to only
839 return from the LE transport start operation once we know the
840 event loop is up and running.
842 struct timespec abs_timeout;
843 if (result == CA_STATUS_OK
844 && clock_gettime(CLOCK_REALTIME, &abs_timeout) == 0)
846 static time_t const relative_timeout = 2; // seconds
847 abs_timeout.tv_sec += relative_timeout;
849 int const wait_result =
850 sem_timedwait(&g_context.le_started, &abs_timeout);
852 if (wait_result == 0)
854 result = CA_STATUS_OK;
861 CAResult_t CAStopLEAdapter()
864 This function is called by the connectivity abstraction when
865 CAUnselectNetwork(CA_ADAPTER_GATT_BTLE) is called by the user.
868 OIC_LOG(DEBUG, TAG, "Stop Linux BLE adapter.");
870 // Only stop if we were previously started.
871 if (!CALECheckStarted())
873 return CA_STATUS_FAILED;
876 CALEStopEventLoop(&g_context);
882 CAResult_t CAGetLEAdapterState()
884 CAResult_t result = CA_ADAPTER_NOT_ENABLED;
886 ca_mutex_lock(g_context.lock);
888 for (GList * l = g_context.adapters; l != NULL; l = l->next)
890 GDBusProxy * const adapter = G_DBUS_PROXY(l->data);
891 GVariant * const prop =
892 g_dbus_proxy_get_cached_property(adapter, "Powered");
896 // This should never happen!
897 result = CA_STATUS_FAILED;
901 gboolean const powered = g_variant_get_boolean(prop);
902 g_variant_unref(prop);
906 result = CA_STATUS_OK;
910 No need to continue iterating since we have at least
911 one enabled Bluetooth adapter.
916 ca_mutex_unlock(g_context.lock);
921 CAResult_t CAInitializeLENetworkMonitor()
924 * @note "Network monitor" operations are started in the
925 * @c CAStartLEAdapter() function rather than this function
926 * due to glib/D-Bus signal handling threads related
929 * @see @c CAStartLEAdapter() for further details.
932 g_context.lock = ca_mutex_new();
933 g_context.condition = ca_cond_new();
935 static int const PSHARED = 0; // shared between threads
936 static unsigned int const VALUE = 0; // force sem_wait() to block
938 if (sem_init(&g_context.le_started, PSHARED, VALUE) != 0)
940 return CA_STATUS_FAILED;
944 The CA LE interface doesn't expose a CAInitializeLEGattServer()
945 function so perform initialization here.
947 CAPeripheralInitialize();
952 void CATerminateLENetworkMonitor()
955 * @note "Network monitor" operations are stopped in @c CALEStop()
956 * since they are started in @c CAStartLEAdapter() rather
957 * than @c CAInitializeLENetworkMonitor().
959 * @see @c CAStartLEAdapter() for further details.
963 Since the CA LE interface doesn't expose a
964 CAInitializeLEGattServer() function, finalize the LE server
965 (peripheral) here rather than in CATerminateLEGattServer() to
966 ensure finalization is correctly paired with initialization.
968 CAPeripheralFinalize();
970 (void) sem_destroy(&g_context.le_started);
972 ca_mutex_lock(g_context.lock);
974 g_context.on_device_state_changed = NULL;
975 g_context.on_server_received_data = NULL;
976 g_context.on_client_received_data = NULL;
977 g_context.client_thread_pool = NULL;
978 g_context.server_thread_pool = NULL;
979 g_context.on_client_error = NULL;
980 g_context.on_server_error = NULL;
982 ca_cond_free(g_context.condition);
983 g_context.condition = NULL;
985 ca_mutex_unlock(g_context.lock);
987 ca_mutex_free(g_context.lock);
988 g_context.lock = NULL;
991 CAResult_t CASetLEAdapterStateChangedCb(
992 CALEDeviceStateChangedCallback callback)
994 ca_mutex_lock(g_context.lock);
995 g_context.on_device_state_changed = callback;
996 ca_mutex_unlock(g_context.lock);
1001 CAResult_t CASetLENWConnectionStateChangedCb(CALEConnectionStateChangedCallback callback)
1004 return CA_NOT_SUPPORTED;
1007 CAResult_t CAGetLEAddress(char **local_address)
1009 OIC_LOG(DEBUG, TAG, "Get Linux BLE local device information.");
1011 if (local_address == NULL)
1013 return CA_STATUS_INVALID_PARAM;
1017 * @bug Attempting to get LE interface information before this
1018 * connectivity abstraction adapter has started (e.g. via
1019 * @c CASelectNetwork()) could result in an inaccurate list
1020 * of LE interfaces. For example, detection of hot-plugged
1021 * bluetooth adapters will only work after the LE IoTivity
1022 * network has been selected. If the LE IoTivity network
1023 * hasn't been selected, the hot-plugged bluetooth adapter
1024 * will not be reflected in the @c CALocalConnectivity_t list
1025 * returned by this function.
1027 * This issue caused by the fact that the event loop that
1028 * handles such events can only be run after this LE
1029 * adapter/transport is started. The event loop cannot be
1030 * started earlier, e.g. during @c CAInitialize(), due to a
1031 * limitation in the CA transport termination code that
1032 * requires thread pools to be destroyed before transport
1033 * termination. If the event loop was added as a task to the
1034 * thread pool during @c CAInitialize(), it's possible that
1035 * the thread running that event loop could be blocked
1036 * waiting for events during a later call to
1037 * @c CATerminate() if @c CASelectNetwork() was not called
1038 * beforehand. The thread pool destruction that occurs
1039 * during @c CATerminate() waits for threads in the pool to
1040 * exit. However, in this case the event loop thread is
1041 * still blocked waiting for events since it may not have
1042 * been stopped since the transport itself was not stopped,
1043 * meaning termination will also be blocked.
1045 * Other than refactoring to the termination code to allow
1046 * thread pools to be started during @c CAInitialize(), the
1047 * only other choice we have to prevent the hang at
1048 * termination is to move the event loop creation and
1049 * termination to the @c CAAdapterStart() and
1050 * @c CAAdapterStop() implementations, respectively, which is
1051 * why we have this bug.
1053 if (!CALECheckStarted())
1054 return CA_ADAPTER_NOT_ENABLED;
1056 *local_address = NULL;
1058 ca_mutex_lock(g_context.lock);
1060 for (GList * l = g_context.adapters; l != NULL; l = l->next)
1062 GDBusProxy * const adapter = G_DBUS_PROXY(l->data);
1065 The local bluetooth adapter MAC address is stored in the
1066 org.bluez.Adapter1.Address property.
1068 GVariant * const prop =
1069 g_dbus_proxy_get_cached_property(adapter, "Address");
1072 Unless the org.bluez.Adapter1.Address property no longer
1073 exists, prop should not be NULL! We have bigger problems if
1074 this assert() is ever tripped since that would mean the
1075 org.bluez.Adapter1 D-Bus interface changed.
1077 assert(prop != NULL);
1079 gchar const * const address = g_variant_get_string(prop, NULL);
1081 *local_address = OICStrdup(address);
1084 No longer need the property variant. The address has been
1087 g_variant_unref(prop);
1090 * @todo Unfortunately the CA LE interface defined in
1091 * caleinterface.h assumes that only one BLE adapter
1092 * will exist on a given host. However, this
1093 * implementation can handle multiple BLE adapters. The
1094 * CA LE interface should be updated so that it can
1095 * handle multiple BLE adapters. For now we'll just
1096 * return the address for the first BLE adapter in the
1102 ca_mutex_unlock(g_context.lock);
1104 return *local_address != NULL ? CA_STATUS_OK : CA_STATUS_FAILED;
1107 CAResult_t CAStartLEGattServer()
1109 return CAPeripheralStart(&g_context);
1112 CAResult_t CAStopLEGattServer()
1114 return CAPeripheralStop();
1117 CAResult_t CAInitializeLEGattServer()
1119 return CA_STATUS_OK;
1122 void CATerminateLEGattServer()
1125 See CATerminateLENetworkMonitor() to understand why the LE
1126 peripheral is not finalized here.
1130 void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback)
1132 ca_mutex_lock(g_context.lock);
1133 g_context.on_server_received_data = callback;
1134 ca_mutex_unlock(g_context.lock);
1137 CAResult_t CAUpdateCharacteristicsToGattClient(char const * address,
1138 uint8_t const * value,
1141 return CAGattServerSendResponseNotification(address,
1146 CAResult_t CAUpdateCharacteristicsToAllGattClients(uint8_t const * value,
1149 return CAGattServerSendResponseNotificationToAll(value, valueLen);
1152 CAResult_t CAStartLEGattClient()
1154 CAResult_t result = CACentralStart(&g_context);
1156 if (result != CA_STATUS_OK)
1161 ca_mutex_lock(g_context.lock);
1162 bool found_peripherals = (g_context.devices != NULL);
1163 ca_mutex_unlock(g_context.lock);
1165 if (!found_peripherals)
1167 // Wait for LE peripherals to be discovered.
1169 // Number of times to wait for discovery to complete.
1170 static int const retries = 5;
1172 static uint64_t const timeout =
1173 2 * MICROSECS_PER_SEC; // Microseconds
1175 if (!CALEWaitForNonEmptyList(&g_context.devices,
1184 Stop discovery so that we can connect to LE peripherals.
1185 Otherwise, the bluetooth subsystem will claim the adapter is
1189 result = CACentralStopDiscovery(&g_context);
1191 if (result != CA_STATUS_OK)
1196 bool const connected = CACentralConnectToAll(&g_context);
1204 * @todo Verify notifications have been enabled on all response
1208 return CAGattClientInitialize(&g_context);
1211 void CAStopLEGattClient()
1213 CAGattClientDestroy();
1214 (void) CACentralStop(&g_context);
1217 CAResult_t CAInitializeLEGattClient()
1219 return CA_STATUS_OK;
1222 void CATerminateLEGattClient()
1226 CAResult_t CAUpdateCharacteristicsToGattServer(
1227 char const * remoteAddress,
1228 uint8_t const * data,
1230 CALETransferType_t type,
1235 if (type != LE_UNICAST)
1237 return CA_STATUS_INVALID_PARAM;
1241 We can assume that we're already connected to the BLE device
1242 with the given remote address - we wouldn't have a remote
1243 address otherwise - so there is no need to start scanning for
1247 return CAGattClientSendData(remoteAddress,
1253 CAResult_t CAUpdateCharacteristicsToAllGattServers(uint8_t const * data,
1257 Now send the request through all BLE connections through the
1258 corresponding OIC GATT request characterstics.
1260 return CAGattClientSendDataToAll(data, length, &g_context);
1263 * @todo Should we restart discovery after the send?
1267 void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback)
1269 ca_mutex_lock(g_context.lock);
1270 g_context.on_client_received_data = callback;
1271 ca_mutex_unlock(g_context.lock);
1274 void CASetLEServerThreadPoolHandle(ca_thread_pool_t handle)
1276 ca_mutex_lock(g_context.lock);
1277 g_context.server_thread_pool = handle;
1278 ca_mutex_unlock(g_context.lock);
1281 void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle)
1283 ca_mutex_lock(g_context.lock);
1284 g_context.client_thread_pool = handle;
1285 ca_mutex_unlock(g_context.lock);
1288 CAResult_t CAUnSetLEAdapterStateChangedCb()
1290 ca_mutex_lock(g_context.lock);
1291 g_context.on_device_state_changed = NULL;
1292 ca_mutex_unlock(g_context.lock);
1294 return CA_STATUS_OK;
1297 void CASetBLEClientErrorHandleCallback(CABLEErrorHandleCallback callback)
1299 ca_mutex_lock(g_context.lock);
1300 g_context.on_client_error = callback;
1301 ca_mutex_unlock(g_context.lock);
1304 void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback)
1306 ca_mutex_lock(g_context.lock);
1307 g_context.on_server_error = callback;
1308 ca_mutex_unlock(g_context.lock);