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);
60 bool const started = (g_context.event_loop != NULL);
62 ca_mutex_unlock(g_context.lock);
65 * @todo Fix potential TOCTOU race condition. A LE transport
66 * adapter could have been started or stopped between the
67 * mutex unlock and boolean check.
72 static void CALEDumpDBusSignalParameters(char const * sender_name,
73 char const * object_path,
74 char const * interface_name,
75 char const * signal_name,
76 GVariant * parameters)
84 gchar * const param_dump =
85 g_variant_print(parameters, TRUE);
92 "\tinterface_name: %s\n"
106 static void CALEOnInterfaceProxyPropertiesChanged(
107 GDBusObjectManagerClient * manager,
108 GDBusObjectProxy * object_proxy,
109 GDBusProxy * interface_proxy,
110 GVariant * changed_properties,
111 gchar const * const * invalidated_properties,
116 (void)invalidated_properties;
119 "Properties Changed on %s:\n",
120 g_dbus_object_get_object_path(
121 G_DBUS_OBJECT(object_proxy)));
123 char const * const interface_name =
124 g_dbus_proxy_get_interface_name(interface_proxy);
126 bool const is_adapter_interface =
127 (strcmp(BLUEZ_ADAPTER_INTERFACE, interface_name) == 0);
129 if (!is_adapter_interface)
132 Only specific org.bluez.Adapter1 property changes are
138 CALEContext * const context = user_data;
141 gchar const * key = NULL;
142 GVariant * value = NULL;
144 g_variant_iter_init(&iter, changed_properties);
145 while (g_variant_iter_next(&iter, "{&sv}", &key, &value))
147 if (strcmp(key, "Powered") == 0)
150 Report a change in the availability of the bluetooth
154 gboolean const powered = g_variant_get_boolean(value);
155 CAAdapterState_t const status =
156 (powered ? CA_ADAPTER_ENABLED : CA_ADAPTER_DISABLED);
160 .adapter = CA_ADAPTER_GATT_BTLE,
163 GVariant * const prop =
164 g_dbus_proxy_get_cached_property(interface_proxy,
167 gchar const * const address = g_variant_get_string(prop, NULL);
169 OICStrcpy(info.addr, sizeof(info.addr), address);
171 g_variant_unref(prop);
174 * @todo Should we acquire the context lock here to
175 * prevent the @c CALEDeviceStateChangedCallback
176 * from being potentially yanked out from under us
177 * if the CA adapters are stopped/terminated as
178 * we're about to invoke this callback?
180 * @todo Unfortunately the CA LE interface defined in
181 * caleinterface.h assumes that only one BLE adapter
182 * will exist on a given host. However, this
183 * implementation can handle multiple BLE adapters.
184 * The CA LE interface should be updated so that it
185 * can handle multiple BLE adapters.
187 context->on_device_state_changed(status);
191 gchar * const s = g_variant_print(value, TRUE);
192 OIC_LOG_V(DEBUG, TAG, " %s -> %s", key, s);
196 g_variant_unref(value);
200 static void CALEHandleInterfaceAdded(GList ** proxy_list,
201 char const * interface,
202 GVariant * parameters)
205 * @note The @a parameters are of the form "(oa{sv})".
207 GDBusProxy * const proxy =
208 CAGetBlueZInterfaceProxy(parameters,
210 g_context.object_manager);
217 ca_mutex_lock(g_context.lock);
220 Add the object information to the list.
222 Note that we prepend instead of append in this case since it
223 is more efficient to do so for linked lists like the one used
226 *proxy_list = g_list_prepend(*proxy_list, proxy);
228 ca_mutex_unlock(g_context.lock);
231 * Let the thread that may be blocked waiting for Devices to be
232 * discovered know that at least one was found.
234 * @todo It doesn't feel good putting this @c org.bluez.Device1
235 * specific code here since this function is meant to be
236 * BlueZ interface neutral. Look into ways of moving this
239 if (strcmp(interface, BLUEZ_DEVICE_INTERFACE) == 0)
241 ca_cond_signal(g_context.condition);
245 static void CALEOnInterfacesAdded(GDBusConnection * connection,
246 char const * sender_name,
247 char const * object_path,
248 char const * interface_name,
249 char const * signal_name,
250 GVariant * parameters,
255 CALEDumpDBusSignalParameters(sender_name,
261 // The signal should always be InterfacesAdded.
262 assert(strcmp(signal_name, "InterfacesAdded") == 0);
264 // Handle addition of a new org.bluez.Adapter1 interface.
265 CALEHandleInterfaceAdded(&g_context.adapters,
266 BLUEZ_ADAPTER_INTERFACE,
269 // Handle addition of a new org.bluez.Device1 interface.
270 CALEHandleInterfaceAdded(&g_context.devices,
271 BLUEZ_DEVICE_INTERFACE,
275 static void CALEOnInterfacesRemoved(GDBusConnection * connection,
276 char const * sender_name,
277 char const * object_path,
278 char const * interface_name,
279 char const * signal_name,
280 GVariant * parameters,
285 CALEDumpDBusSignalParameters(sender_name,
291 // The signal should always be InterfacesRemoved.
292 assert(strcmp(signal_name, "InterfacesRemoved") == 0);
295 The object path is first tuple element, and the interface names
296 the second. Check if "org.bluez.Adapter1" exists in the
297 interface array. If it does, remove the corresponding
298 information from the adapter_infos list.
300 GVariant * const interfaces =
301 g_variant_get_child_value(parameters, 1);
303 GVariantIter * iter = NULL;
304 g_variant_get(interfaces, "as", &iter);
307 * Iterate over the array and remove all BlueZ interface proxies
308 * with a matching D-Bus object path from the corresponding list.
310 * @todo Determine whether we should optimize this nested loop.
311 * It may not be worthwhile to do so since the lists being
312 * iterated over should be very short.
314 for (GVariant * child = g_variant_iter_next_value(iter);
316 child = g_variant_iter_next_value(iter))
318 char const * interface = NULL;
319 g_variant_get(child, "&s", &interface);
321 GList ** list = NULL;
323 if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0)
325 list = &g_context.adapters;
327 else if (strcmp(interface, BLUEZ_DEVICE_INTERFACE) == 0)
329 list = &g_context.devices;
336 // The object path is the first tuple element.
337 gchar const * path = NULL;
338 g_variant_get_child(parameters, 0, "&o", &path);
340 ca_mutex_lock(g_context.lock);
342 for (GList * l = *list; l != NULL; l = g_list_next(l))
344 GDBusProxy * const proxy = G_DBUS_PROXY(l->data);
347 g_dbus_proxy_get_object_path(proxy)) == 0)
350 g_object_unref(proxy);
352 *list = g_list_delete_link(*list, l);
358 ca_mutex_unlock(g_context.lock);
360 g_variant_unref(child);
365 g_variant_iter_free(iter);
369 static void CALEOnPropertiesChanged(GDBusConnection * connection,
370 char const * sender_name,
371 char const * object_path,
372 char const * interface_name,
373 char const * signal_name,
374 GVariant * parameters,
379 CALEDumpDBusSignalParameters(sender_name,
386 static void CALEOnPropertyChanged(GDBusConnection * connection,
387 char const * sender_name,
388 char const * object_path,
389 char const * interface_name,
390 char const * signal_name,
391 GVariant * parameters,
396 CALEDumpDBusSignalParameters(sender_name,
403 static void CALESubscribeToSignals(CALEContext * context,
404 GDBusConnection * connection,
405 GDBusObjectManager * object_manager)
407 static char const om_interface[] =
408 "org.freedesktop.DBus.ObjectManager";
411 Subscribe to D-Bus signals that will allow us to detect changes
412 in BlueZ adapter and device properties.
414 guint const interfaces_added_sub_id =
415 g_dbus_connection_signal_subscribe(
422 G_DBUS_SIGNAL_FLAGS_NONE,
423 CALEOnInterfacesAdded,
427 guint const interfaces_removed_sub_id =
428 g_dbus_connection_signal_subscribe(
435 G_DBUS_SIGNAL_FLAGS_NONE,
436 CALEOnInterfacesRemoved,
440 #if GLIB_CHECK_VERSION(2,38,0)
442 The G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH flag was introduced in
445 static GDBusSignalFlags const device_signal_flags =
446 G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH;
448 static GDBusSignalFlags const device_signal_flags =
449 G_DBUS_SIGNAL_FLAGS_NONE;
453 * @todo Verify that this signal subscription is needed.
455 * @bug The arg0 argument below should be a D-Bus object path, not
458 guint const properties_changed_sub_id =
459 g_dbus_connection_signal_subscribe(
462 "org.freedesktop.DBus.Properties",
465 "org.bluez.Device1", // arg0
467 CALEOnPropertiesChanged,
472 * @todo Verify that this signal subscription is needed.
474 guint const property_changed_sub_id =
475 g_dbus_connection_signal_subscribe(connection,
477 BLUEZ_ADAPTER_INTERFACE,
481 G_DBUS_SIGNAL_FLAGS_NONE,
482 CALEOnPropertyChanged,
486 g_signal_connect(object_manager,
487 "interface-proxy-properties-changed",
488 G_CALLBACK(CALEOnInterfaceProxyPropertiesChanged),
491 ca_mutex_lock(context->lock);
493 context->interfaces_added_sub_id = interfaces_added_sub_id;
494 context->interfaces_removed_sub_id = interfaces_removed_sub_id;
495 context->properties_changed_sub_id = properties_changed_sub_id;
496 context->property_changed_sub_id = property_changed_sub_id;
498 ca_mutex_unlock(context->lock);
501 static bool CALESetUpDBus(CALEContext * context)
503 assert(context != NULL);
505 bool success = false;
507 GError * error = NULL;
510 Set up connection to the D-Bus system bus, where the BlueZ
513 GDBusConnection * const connection =
514 g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
516 if (connection == NULL)
520 "Connection to D-Bus system bus failed: %s.",
528 // Create a proxy to the BlueZ D-Bus ObjectManager.
529 static char const object_manager_path[] = "/";
531 GDBusObjectManager * const object_manager =
532 g_dbus_object_manager_client_new_sync(
534 G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
537 NULL, // get_proxy_type_func
538 NULL, // get_proxy_type_user_data
539 NULL, // get_proxy_type_destroy_notify
543 if (object_manager == NULL)
547 "Unable to create D-Bus ObjectManager client: %s",
552 g_object_unref(connection);
557 CALESubscribeToSignals(context, connection, object_manager);
559 ca_mutex_lock(context->lock);
560 context->connection = connection;
561 context->object_manager = object_manager;
562 ca_mutex_unlock(context->lock);
564 success = CALESetUpBlueZObjects(context);
569 static void CALETearDownDBus(CALEContext * context)
571 assert(context != NULL);
574 Minimize the time we hold the global lock by only clearing the
575 global state, and pushing resource finalization outside the global
578 ca_mutex_lock(context->lock);
580 GDBusConnection * const connection = context->connection;
581 context->connection = NULL;
583 GDBusObjectManager * const object_manager = context->object_manager;
584 context->object_manager = NULL;
586 GList * const objects = context->objects;
587 context->objects = NULL;
589 GList * const adapters = context->adapters;
590 context->adapters = NULL;
592 GList * const devices = context->devices;
593 context->devices = NULL;
595 guint const interfaces_added = context->interfaces_added_sub_id;
596 guint const interfaces_removed = context->interfaces_removed_sub_id;
597 guint const properties_changed = context->properties_changed_sub_id;
598 guint const property_changed = context->property_changed_sub_id;
600 context->interfaces_added_sub_id = 0;
601 context->interfaces_removed_sub_id = 0;
602 context->properties_changed_sub_id = 0;
603 context->property_changed_sub_id = 0;
605 ca_mutex_unlock(context->lock);
607 // Destroy the device proxies list.
608 g_list_free_full(devices, g_object_unref);
610 // Destroy the adapter proxies list.
611 g_list_free_full(adapters, g_object_unref);
613 // Destroy the list of objects obtained from the ObjectManager.
614 g_list_free_full(objects, g_object_unref);
616 // Destroy the ObjectManager proxy.
617 if (object_manager != NULL)
619 g_object_unref(object_manager);
622 // Tear down the D-Bus connection to the system bus.
623 if (connection != NULL)
625 g_dbus_connection_signal_unsubscribe(connection,
627 g_dbus_connection_signal_unsubscribe(connection,
629 g_dbus_connection_signal_unsubscribe(connection,
631 g_dbus_connection_signal_unsubscribe(connection,
633 g_object_unref(connection);
637 static bool CALEDeviceFilter(GDBusProxy * device)
639 bool accepted = false;
642 Filter out any devices that don't support the OIC Transport
645 GVariant * const prop =
646 g_dbus_proxy_get_cached_property(device, "UUIDs");
650 // No remote services available on the device.
655 char const ** const UUIDs = g_variant_get_strv(prop, &length);
658 It would have been nice to use g_strv_contains() here, but we
659 would need to run it twice: once for the uppercase form of the
660 UUID and once for for the lowercase form. Just run the loop
661 manually, and use strcasecmp() instead.
663 char const * const * const end = UUIDs + length;
664 for (char const * const * u = UUIDs; u != end; ++u)
666 if (strcasecmp(*u, CA_GATT_SERVICE_UUID) == 0)
674 g_variant_unref(prop);
680 static bool CALESetUpBlueZObjects(CALEContext * context)
682 bool success = false;
684 // Get the list of BlueZ D-Bus objects.
685 GList * const objects =
686 g_dbus_object_manager_get_objects(context->object_manager);
688 if (objects == NULL) {
691 "Unable to get objects from ObjectManager.");
696 ca_mutex_lock(context->lock);
697 context->objects = objects;
698 ca_mutex_unlock(context->lock);
701 Create a proxies to the org.bluez.Adapter1 D-Bus objects that
702 will later be used to obtain local bluetooth adapter properties,
703 as well as by the BLE central code to discover peripherals.
705 GList * adapters = NULL;
706 success = CAGetBlueZManagedObjectProxies(&adapters,
707 BLUEZ_ADAPTER_INTERFACE,
711 // An empty adapters list is NULL.
712 if (success && adapters != NULL)
714 ca_mutex_lock(context->lock);
715 context->adapters = adapters;
716 ca_mutex_unlock(context->lock);
720 Create a proxies to the org.bluez.Device1 D-Bus objects that
721 will later be used to establish connections.
723 GList * devices = NULL;
724 success = CAGetBlueZManagedObjectProxies(&devices,
725 BLUEZ_DEVICE_INTERFACE,
729 // An empty device list is NULL.
730 if (success && devices != NULL)
732 ca_mutex_lock(context->lock);
733 context->devices = devices;
734 ca_mutex_unlock(context->lock);
737 /* success = CAGattClientInitialize(context); */
742 static void CALEStartEventLoop(void * data)
744 CALEContext * const context = data;
746 assert(context != NULL);
748 // Create the event loop.
749 GMainContext * const loop_context = g_main_context_new();
750 GMainLoop * const event_loop = g_main_loop_new(loop_context, FALSE);
752 ca_mutex_lock(context->lock);
754 assert(context->event_loop == NULL);
755 context->event_loop = event_loop;
757 ca_mutex_unlock(context->lock);
759 g_main_context_push_thread_default(loop_context);
762 We have to do the BlueZ object manager client initialization and
763 signal subscription here so that the corresponding asynchronous
764 signal handling occurs in the same thread as the one running the
767 if (!CALESetUpDBus(&g_context))
770 ca_cond_signal(g_context.condition);
772 g_main_loop_run(event_loop);
775 static void CALEStopEventLoop(CALEContext * context)
777 ca_mutex_lock(context->lock);
779 GMainLoop * const event_loop = context->event_loop;
780 context->event_loop = NULL;
782 ca_mutex_unlock(context->lock);
784 if (event_loop != NULL)
786 g_main_loop_quit(event_loop);
788 GMainContext * const loop_context =
789 g_main_loop_get_context(event_loop);
791 if (loop_context != NULL)
793 g_main_context_wakeup(loop_context);
794 g_main_context_unref(loop_context);
797 g_main_loop_unref(event_loop);
802 * Wait for @a list to be non-empty.
804 * @param[in] list List that should not be empty.
805 * @param[in] retries Number of times to retry if the @a timeout is
807 * @param[in] timeout Timeout in microseconds to wait between retries.
809 static bool CALEWaitForNonEmptyList(GList * const * list,
813 bool success = false;
815 ca_mutex_lock(g_context.lock);
817 for (int i = 0; *list == NULL && i < retries; ++i)
819 if (ca_cond_wait_for(g_context.condition,
824 Condition variable was signaled before the timeout was
831 ca_mutex_unlock(g_context.lock);
836 static CAResult_t CALEStop()
838 CAResult_t result = CA_STATUS_FAILED;
840 OIC_LOG(DEBUG, TAG, "Stop Linux BLE adapter.");
842 // Only stop if we were previously started.
843 if (!CALECheckStarted())
848 // Stop the event loop thread regardless of previous errors.
849 CALEStopEventLoop(&g_context);
851 CALETearDownDBus(&g_context);
856 static void CALETerminate()
858 OIC_LOG(DEBUG, TAG, "Terminate BLE adapter.");
860 CAPeripheralFinalize();
862 ca_mutex_lock(g_context.lock);
864 g_context.on_device_state_changed = NULL;
865 g_context.on_server_received_data = NULL;
866 g_context.on_client_received_data = NULL;
867 g_context.client_thread_pool = NULL;
868 g_context.server_thread_pool = NULL;
869 g_context.on_client_error = NULL;
870 g_context.on_server_error = NULL;
872 ca_mutex_unlock(g_context.lock);
874 ca_cond_free(g_context.condition);
875 ca_mutex_free(g_context.lock);
878 // -----------------------------------------------------------------------
880 CAResult_t CAInitializeLEAdapter()
882 #if !GLIB_CHECK_VERSION(2,36,0)
884 Initialize the GLib type system.
886 As of GLib 2.36, it is no longer necessary to explicitly call
892 g_context.lock = ca_mutex_new();
893 g_context.condition = ca_cond_new();
895 CAPeripheralInitialize();
900 CAResult_t CAStartLEAdapter()
903 This function is called by the connectivity abstraction when
904 CASelectNetwork(CA_ADAPTER_GATT_BTLE) is called by the user.
907 OIC_LOG(DEBUG, TAG, __func__);
909 CAResult_t result = CA_STATUS_FAILED;
911 // Only start if we were previously stopped.
912 if (CALECheckStarted())
918 * Spawn a thread to run the GLib event loop that will drive D-Bus
921 * @note Ideally this should be done in the @c CAInitializeLE()
922 * function so that we can detect local bluetooth adapter
923 * changes right away, without having to first start this LE
924 * adapter/transport via @cCASelectNetwork(). However, a
925 * limitation in the CA termination code that destroys the
926 * thread pool before the transport adapters prevents us
927 * from doing that without potentially triggering a
928 * @c pthread_join() call that blocks indefinitely due to
929 * this event loop not be stopped. See the comments in the
930 * @c CAGetLEInterfaceInformation() function below for
933 result = ca_thread_pool_add_task(g_context.client_thread_pool,
937 if (result != CA_STATUS_OK)
943 Wait until initialization completes before continuing, basically
944 until some Bluetooth adapters were found.
947 // Number of times to wait for initialization to complete.
948 static int const retries = 2;
950 static uint64_t const timeout =
951 2 * MICROSECS_PER_SEC; // Microseconds
953 if (CALEWaitForNonEmptyList(&g_context.adapters, retries, timeout))
955 result = CA_STATUS_OK;
961 CAResult_t CAGetLEAdapterState()
964 * @todo To be implemented shortly as part of the effort to
965 * address a critical code review that stated this BLE
966 * transport should implement the interface defined in
969 return CA_NOT_SUPPORTED;
972 CAResult_t CAInitializeLENetworkMonitor()
975 * @todo To be implemented shortly as part of the effort to
976 * address a critical code review that stated this BLE
977 * transport should implement the interface defined in
983 void CATerminateLENetworkMonitor()
986 * @todo To be implemented shortly as part of the effort to
987 * address a critical code review that stated this BLE
988 * transport should implement the interface defined in
993 CAResult_t CASetLEAdapterStateChangedCb(CALEDeviceStateChangedCallback callback)
995 ca_mutex_lock(g_context.lock);
996 g_context.on_device_state_changed = callback;
997 ca_mutex_unlock(g_context.lock);
1002 CAResult_t CAInitLENetworkMonitorMutexVariables()
1005 This CA LE interface implementation doesn't use a network
1006 monitor as the other platform implementationd do.
1008 return CA_STATUS_OK;
1011 void CATerminateLENetworkMonitorMutexVariables()
1014 This CA LE interface implementation doesn't use a network
1015 monitor as the other platform implementationd do.
1019 CAResult_t CAGetLEAddress(char **local_address)
1021 OIC_LOG(DEBUG, TAG, "Get Linux BLE local device information.");
1023 if (local_address == NULL)
1025 return CA_STATUS_INVALID_PARAM;
1029 * @bug Attempting to get LE interface information before this
1030 * connectivity abstraction adapter has started (e.g. via
1031 * @c CASelectNetwork()) could result in an inaccurate list
1032 * of LE interfaces. For example, detection of hot-plugged
1033 * bluetooth adapters will only work after the LE IoTivity
1034 * network has been selected. If the LE IoTivity network
1035 * hasn't been selected, the hot-plugged bluetooth adapter
1036 * will not be reflected in the @c CALocalConnectivity_t list
1037 * returned by this function.
1039 * This issue caused by the fact that the event loop that
1040 * handles such events can only be run after this LE
1041 * adapter/transport is started. The event loop cannot be
1042 * started earlier, e.g. during @c CAInitialize(), due to a
1043 * limitation in the CA transport termination code that
1044 * requires thread pools to be destroyed before transport
1045 * termination. If the event loop was added as a task to the
1046 * thread pool during @c CAInitialize(), it's possible that
1047 * the thread running that event loop could be blocked
1048 * waiting for events during a later call to
1049 * @c CATerminate() if @c CASelectNetwork() was not called
1050 * beforehand. The thread pool destruction that occurs
1051 * during @c CATerminate() waits for threads in the pool to
1052 * exit. However, in this case the event loop thread is
1053 * still blocked waiting for events since it may not have
1054 * been stopped since the transport itself was not stopped,
1055 * meaning termination will also be blocked.
1057 * Other than refactoring to the termination code to allow
1058 * thread pools to be started during @c CAInitialize(), the
1059 * only other choice we have to prevent the hang at
1060 * termination is to move the event loop creation and
1061 * termination to the @c CAAdapterStart() and
1062 * @c CAAdapterStop() implementations, respectively, which is
1063 * why we have this bug.
1065 if (!CALECheckStarted())
1066 return CA_ADAPTER_NOT_ENABLED;
1068 *local_address = NULL;
1070 ca_mutex_lock(g_context.lock);
1072 for (GList * l = g_context.adapters; l != NULL; l = l->next)
1074 GDBusProxy * const adapter = G_DBUS_PROXY(l->data);
1077 The local bluetooth adapter MAC address is stored in the
1078 org.bluez.Adapter1.Address property.
1080 GVariant * const prop =
1081 g_dbus_proxy_get_cached_property(adapter, "Address");
1084 Unless the org.bluez.Adapter1.Address property no longer
1085 exists, prop should not be NULL! We have bigger problems if
1086 this assert() is ever tripped since that would mean the
1087 org.bluez.Adapter1 D-Bus interface changed.
1089 assert(prop != NULL);
1091 gchar const * const address = g_variant_get_string(prop, NULL);
1093 *local_address = OICStrdup(address);
1096 No longer need the property variant. The address has been
1099 g_variant_unref(prop);
1102 * @todo Unfortunately the CA LE interface defined in
1103 * caleinterface.h assumes that only one BLE adapter
1104 * will exist on a given host. However, this
1105 * implementation can handle multiple BLE adapters. The
1106 * CA LE interface should be updated so that it can
1107 * handle multiple BLE adapters. For now we'll just
1108 * return the address for the first BLE adapter in the
1114 ca_mutex_unlock(g_context.lock);
1116 return *local_address != NULL ? CA_STATUS_OK : CA_STATUS_FAILED;
1119 CAResult_t CAStartLEGattServer()
1121 return CAPeripheralStart(&g_context);
1124 CAResult_t CAStopLEGattServer()
1126 CAResult_t result = CAPeripheralStop();
1127 CAResult_t const tmp = CALEStop();
1129 if (result == CA_STATUS_OK && tmp != CA_STATUS_OK)
1137 void CATerminateLEGattServer()
1142 void CASetLEReqRespServerCallback(CABLEDataReceivedCallback callback)
1144 ca_mutex_lock(g_context.lock);
1145 g_context.on_server_received_data = callback;
1146 ca_mutex_unlock(g_context.lock);
1149 CAResult_t CAUpdateCharacteristicsToGattClient(char const * address,
1150 uint8_t const * value,
1157 * @todo To be implemented shortly as part of the effort to
1158 * address a critical code review that stated this BLE
1159 * transport should implement the interface defined in
1162 return CA_NOT_SUPPORTED;
1165 CAResult_t CAUpdateCharacteristicsToAllGattClients(uint8_t const * value,
1171 * @todo To be implemented shortly as part of the effort to
1172 * address a critical code review that stated this BLE
1173 * transport should implement the interface defined in
1176 return CA_NOT_SUPPORTED;
1179 CAResult_t CAStartLEGattClient()
1181 return CACentralStart(&g_context);
1184 void CAStopLEGattClient()
1186 (void) CACentralStop(&g_context);
1190 void CATerminateLEGattClient()
1195 void CACheckLEData()
1198 This function is only used in single-threaded builds, but this
1199 CA LE adapter implementation is multi-threaded. Consequently,
1200 this function is a no-op.
1204 CAResult_t CAUpdateCharacteristicsToGattServer(
1205 char const * remoteAddress,
1206 uint8_t const * data,
1208 CALETransferType_t type,
1211 (void)remoteAddress;
1217 * @todo To be implemented shortly as part of the effort to
1218 * address a critical code review that stated this BLE
1219 * transport should implement the interface defined in
1222 return CA_NOT_SUPPORTED;
1225 CAResult_t CAUpdateCharacteristicsToAllGattServers(uint8_t const * data,
1228 OIC_LOG(DEBUG, TAG, "Send data to all");
1231 Multicast data is only sent when a request is sent from a client
1232 across all endpoints. We need not worry about sending a
1233 response from a server here.
1236 CAResult_t result = CA_STATUS_FAILED;
1238 ca_mutex_lock(g_context.lock);
1239 bool found_peripherals = (g_context.devices != NULL);
1240 ca_mutex_unlock(g_context.lock);
1242 if (!found_peripherals)
1245 Start discovery of LE peripherals that advertise the OIC
1248 result = CACentralStartDiscovery(&g_context);
1250 if (result != CA_STATUS_OK)
1255 // Wait for LE peripherals to be discovered.
1257 // Number of times to wait for discovery to complete.
1258 static int const retries = 5;
1260 static uint64_t const timeout =
1261 2 * MICROSECS_PER_SEC; // Microseconds
1263 if (!CALEWaitForNonEmptyList(&g_context.devices,
1270 ca_mutex_lock(g_context.lock);
1271 found_peripherals = (g_context.devices == NULL);
1272 ca_mutex_unlock(g_context.lock);
1274 if (!found_peripherals)
1276 // No peripherals discovered!
1282 Stop discovery so that we can connect to LE peripherals.
1283 Otherwise, the bluetooth subsystem will claim the adapter is
1287 result = CACentralStopDiscovery(&g_context);
1289 if (result != CA_STATUS_OK)
1294 bool const connected = CACentralConnectToAll(&g_context);
1302 * @todo Start notifications on all response characteristics.
1306 Now send the request through all BLE connections through the
1307 corresponding OIC GATT request characterstics.
1310 CAGattRequestInfo const info =
1312 .characteristic_info = NULL, // g_context.characteristics
1313 .context = &g_context
1316 return CAGattClientSendDataToAll(&info, data, length);
1319 * @todo Should we restart discovery after the send?
1323 void CASetLEReqRespClientCallback(CABLEDataReceivedCallback callback)
1325 ca_mutex_lock(g_context.lock);
1326 g_context.on_client_received_data = callback;
1327 ca_mutex_unlock(g_context.lock);
1330 void CASetLEServerThreadPoolHandle(ca_thread_pool_t handle)
1332 ca_mutex_lock(g_context.lock);
1333 g_context.server_thread_pool = handle;
1334 ca_mutex_unlock(g_context.lock);
1337 void CASetLEClientThreadPoolHandle(ca_thread_pool_t handle)
1339 ca_mutex_lock(g_context.lock);
1340 g_context.client_thread_pool = handle;
1341 ca_mutex_unlock(g_context.lock);
1344 CAResult_t CAUnSetLEAdapterStateChangedCb()
1346 ca_mutex_lock(g_context.lock);
1347 g_context.on_device_state_changed = NULL;
1348 ca_mutex_unlock(g_context.lock);
1350 return CA_STATUS_OK;
1353 void CASetBLEClientErrorHandleCallback(CABLEErrorHandleCallback callback)
1355 ca_mutex_lock(g_context.lock);
1356 g_context.on_client_error = callback;
1357 ca_mutex_unlock(g_context.lock);
1360 void CASetBLEServerErrorHandleCallback(CABLEErrorHandleCallback callback)
1362 ca_mutex_lock(g_context.lock);
1363 g_context.on_server_error = callback;
1364 ca_mutex_unlock(g_context.lock);