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 ******************************************************************/
23 #include "cagattservice.h" // For CA_GATT_SERVICE_UUID.
31 static char const TAG[] = "BLE_CENTRAL";
33 static bool CACentralGetBooleanProperty(GDBusProxy * device,
34 char const * property)
36 GVariant * const cached_property =
37 g_dbus_proxy_get_cached_property(device, property);
39 if (cached_property == NULL)
44 bool const value = g_variant_get_boolean(cached_property);
46 g_variant_unref(cached_property);
51 static void CACentralStartDiscoveryImpl(gpointer proxy, gpointer user_data)
53 assert(proxy != NULL);
54 assert(user_data != NULL);
56 GDBusProxy * const adapter = G_DBUS_PROXY(proxy);
57 CAResult_t * const result = user_data;
59 *result = CA_STATUS_FAILED;
61 bool const is_discovering =
62 CACentralGetBooleanProperty(adapter, "Discovering");
66 // Nothing to do. Avoid invoking a method over D-Bus.
67 *result = CA_STATUS_OK;
72 // Make sure the adapter is powered on before starting discovery.
73 if (!CASetBlueZObjectProperty(adapter,
74 BLUEZ_ADAPTER_INTERFACE,
76 g_variant_new_boolean(TRUE)))
80 "Unable to power on LE central adapter.");
86 Only scan for LE peripherals that advertise the OIC Transport
87 Profile GATT service UUID by setting a discovery filter on the BlueZ
88 org.bluez.Adapter1 object with two parameters:
90 (1) "UUIDs": set to an array containing that OIC Transport
91 Profile GATT service UUID
92 (2) "Transport": set to "le"
94 See the documentation for the SetDiscoveryFilter() method in the
95 BlueZ `adapter-api.txt' document for more details.
97 GVariantBuilder builder;
98 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
100 static char const * const UUIDs[] =
105 g_variant_builder_add(&builder,
110 sizeof(UUIDs) / sizeof(UUIDs[0])));
112 g_variant_builder_add(&builder,
115 g_variant_new_string("le"));
117 GVariant * const filter = g_variant_builder_end(&builder);
120 SetDiscoveryFilter() expects a dictionary but it must be packed
121 into a tuple for the actual call through the proxy.
123 GVariant * const filter_parameters =
124 g_variant_new("(@a{sv})", filter);
126 GError * error = NULL;
129 This is a synchronous call, but the actually discovery is
130 performed asynchronously. org.bluez.Device1 objects will
132 org.freedesktop.DBus.ObjectManager.InterfacesAdded signal as
133 peripherals that match our discovery filter criteria are found.
136 g_dbus_proxy_call_sync(adapter,
137 "SetDiscoveryFilter",
139 G_DBUS_CALL_FLAGS_NONE,
140 -1, // timeout (default == -1),
148 "SetDiscoveryFilter() call failed: %s",
156 g_variant_unref(ret);
158 // Start device discovery.
159 ret = g_dbus_proxy_call_sync(adapter,
162 G_DBUS_CALL_FLAGS_NONE,
163 -1, // timeout (default == -1),
171 "StartDiscovery() call failed: %s",
179 g_variant_unref(ret);
181 *result = CA_STATUS_OK;
184 static void CACentralStopDiscoveryImpl(gpointer proxy, gpointer user_data)
186 assert(proxy != NULL);
187 assert(user_data != NULL);
189 GDBusProxy * const adapter = G_DBUS_PROXY(proxy);
190 CAResult_t * const result = user_data;
192 bool const is_discovering =
193 CACentralGetBooleanProperty(adapter, "Discovering");
197 // Nothing to do. Avoid invoking a method over D-Bus.
198 *result = CA_STATUS_OK;
202 *result = CA_STATUS_FAILED;
205 GError * error = NULL;
207 // Stop discovery sessions.
208 GVariant * const ret =
209 g_dbus_proxy_call_sync(adapter,
212 G_DBUS_CALL_FLAGS_NONE,
213 -1, // timeout (default == -1)
221 "StopDiscovery() call failed: %s",
229 g_variant_unref(ret);
231 *result = CA_STATUS_OK;
234 static void CACentralConnectToDevice(gpointer data, gpointer user_data)
236 assert(data != NULL);
237 assert(user_data != NULL);
239 GDBusProxy * const device = G_DBUS_PROXY(data);
240 bool * const connected = user_data;
242 if (!CACentralConnect(device))
249 * Disconnect from all LE peripherals.
251 * @param[in] context Context containing BlueZ device information.
253 static void CACentralDisconnect(CALEContext * context)
255 assert(context != NULL);
257 oc_mutex_lock(context->lock);
259 for (GList * l = context->devices; l != NULL; l = l->next)
261 GDBusProxy * const device = G_DBUS_PROXY(l->data);
263 bool const is_connected =
264 CACentralGetBooleanProperty(device, "Connected");
269 Asynchronously disconnect. We don't care about the
270 result so don't bother passing in a callback.
272 g_dbus_proxy_call(device,
275 G_DBUS_CALL_FLAGS_NONE,
276 -1, // timeout (default == -1),
283 oc_mutex_unlock(context->lock);
286 // -----------------------------------------------------------------------
288 CAResult_t CACentralStart(CALEContext * context)
290 assert(context != NULL);
292 CAResult_t result = CA_STATUS_FAILED;
295 Synchronize access to the adapter information using the base
296 context lock since we don't own the adapters.
298 oc_mutex_lock(context->lock);
301 * Start discovery on all detected adapters.
303 * @todo The current start start_discovery() implementation makes
304 * two synchronous D-Bus calls to each BlueZ @c Adapter1
305 * object in the @a adapters list. We may want to make them
306 * asynchronous to minimize blocking.
308 g_list_foreach(context->adapters,
309 CACentralStartDiscoveryImpl,
312 oc_mutex_unlock(context->lock);
317 CAResult_t CACentralStop(CALEContext * context)
319 assert(context != NULL);
321 CAResult_t result = CA_STATUS_FAILED;
323 // Stop discovery on all detected adapters.
324 result = CACentralStopDiscovery(context);
326 // Disconnect from all adapters.
327 CACentralDisconnect(context);
330 * @todo Stop notifications on all response characteristics.
336 CAResult_t CACentralStartDiscovery(CALEContext * context)
338 assert(context != NULL);
340 CAResult_t result = CA_STATUS_FAILED;
343 Synchronize access to the adapter information using the base
344 context lock since we don't own the adapters.
346 oc_mutex_lock(context->lock);
348 // Start discovery on all detected adapters.
349 g_list_foreach(context->adapters,
350 CACentralStartDiscoveryImpl,
353 oc_mutex_unlock(context->lock);
358 CAResult_t CACentralStopDiscovery(CALEContext * context)
360 assert(context != NULL);
362 CAResult_t result = CA_STATUS_FAILED;
365 Synchronize access to the adapter information using the base
366 context lock since we don't own the adapters.
368 oc_mutex_lock(context->lock);
370 // Stop discovery on all detected adapters.
371 g_list_foreach(context->adapters,
372 CACentralStopDiscoveryImpl,
376 * @todo Stop notifications on all response characteristics.
379 oc_mutex_unlock(context->lock);
384 bool CACentralConnect(GDBusProxy * device)
386 assert(device != NULL);
389 Check if a connection to the LE peripheral was already
390 established. If not, establish a connection.
392 bool const is_connected =
393 CACentralGetBooleanProperty(device, "Connected");
400 GError * error = NULL;
402 // Connect to the discovered LE peripheral asynchronously.
403 GVariant * const ret =
404 g_dbus_proxy_call_sync(device,
407 G_DBUS_CALL_FLAGS_NONE,
408 -1, // timeout (default == -1),
416 "%s.Connect() call failed: %s",
417 BLUEZ_DEVICE_INTERFACE,
425 g_variant_unref(ret);
430 bool CACentralConnectToAll(CALEContext * context)
432 bool connected = true;
434 oc_mutex_lock(context->lock);
436 // Connect to the LE peripherals, if we're not already connected.
437 g_list_foreach(context->devices,
438 CACentralConnectToDevice,
441 oc_mutex_unlock(context->lock);