Imported Upstream version 1.0.0
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / bt_le_adapter / linux / central.c
1 /******************************************************************
2  *
3  * Copyright 2015 Intel Corporation All Rights Reserved.
4  *
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  ******************************************************************/
18
19 #include "central.h"
20 #include "utils.h"
21 #include "bluez.h"
22
23 #include "cagattservice.h"  // For CA_GATT_SERVICE_UUID.
24 #include "logger.h"
25
26 #include <stdbool.h>
27 #include <assert.h>
28
29
30 // Logging tag.
31 static char const TAG[] = "BLE_CENTRAL";
32
33 static bool CACentralGetBooleanProperty(GDBusProxy * device,
34                                         char const * property)
35 {
36     GVariant * const cached_property =
37         g_dbus_proxy_get_cached_property(device, property);
38
39     if (cached_property == NULL)
40     {
41         return false;
42     }
43
44     bool const value = g_variant_get_boolean(cached_property);
45
46     g_variant_unref(cached_property);
47
48     return value;
49 }
50
51 static void CACentralStartDiscoveryImpl(gpointer proxy, gpointer user_data)
52 {
53     assert(proxy != NULL);
54     assert(user_data != NULL);
55
56     GDBusProxy * const adapter = G_DBUS_PROXY(proxy);
57     CAResult_t * const result  = user_data;
58
59     *result = CA_STATUS_FAILED;
60
61     bool const is_discovering =
62         CACentralGetBooleanProperty(adapter, "Discovering");
63
64     if (is_discovering)
65     {
66         // Nothing to do.  Avoid invoking a method over D-Bus.
67         *result = CA_STATUS_OK;
68         return;
69     }
70
71
72     // Make sure the adapter is powered on before starting discovery.
73     if (!CASetBlueZObjectProperty(adapter,
74                                   BLUEZ_ADAPTER_INTERFACE,
75                                   "Powered",
76                                   g_variant_new_boolean(TRUE)))
77     {
78         OIC_LOG(ERROR,
79                 TAG,
80                 "Unable to power on LE central adapter.");
81
82         return;
83     }
84
85     /*
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:
89
90           (1) "UUIDs": set to an array containing that OIC Transport
91                        Profile GATT service UUID
92           (2) "Transport": set to "le"
93
94       See the documentation for the SetDiscoveryFilter() method in the
95       BlueZ `adapter-api.txt' document for more details.
96     */
97     GVariantBuilder builder;
98     g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
99
100     static char const * const UUIDs[] =
101         {
102             CA_GATT_SERVICE_UUID
103         };
104
105     g_variant_builder_add(&builder,
106                           "{sv}",
107                           "UUIDs",
108                           g_variant_new_strv(
109                               UUIDs,
110                               sizeof(UUIDs) / sizeof(UUIDs[0])));
111
112     g_variant_builder_add(&builder,
113                           "{sv}",
114                           "Transport",
115                           g_variant_new_string("le"));
116
117     GVariant * const filter = g_variant_builder_end(&builder);
118
119     /*
120       SetDiscoveryFilter() expects a dictionary but it must be packed
121       into a tuple for the actual call through the proxy.
122     */
123     GVariant * const filter_parameters =
124         g_variant_new("(@a{sv})", filter);
125
126     GError * error = NULL;
127
128     /*
129       This is a synchronous call, but the actually discovery is
130       performed asynchronously.  org.bluez.Device1 objects will
131       reported through the
132       org.freedesktop.DBus.ObjectManager.InterfacesAdded signal as
133       peripherals that match our discovery filter criteria are found.
134      */
135     GVariant * ret =
136         g_dbus_proxy_call_sync(adapter,
137                                "SetDiscoveryFilter",
138                                filter_parameters,
139                                G_DBUS_CALL_FLAGS_NONE,
140                                -1,    // timeout (default == -1),
141                                NULL,  // cancellable
142                                &error);
143
144     if (ret == NULL)
145     {
146         OIC_LOG_V(ERROR,
147                   TAG,
148                   "SetDiscoveryFilter() call failed: %s",
149                   error->message);
150
151         g_error_free(error);
152
153         return;
154     }
155
156     g_variant_unref(ret);
157
158     // Start device discovery.
159     ret = g_dbus_proxy_call_sync(adapter,
160                                  "StartDiscovery",
161                                  NULL,  // parameters
162                                  G_DBUS_CALL_FLAGS_NONE,
163                                  -1,    // timeout (default == -1),
164                                  NULL,  // cancellable
165                                  &error);
166
167     if (ret == NULL)
168     {
169         OIC_LOG_V(ERROR,
170                   TAG,
171                   "StartDiscovery() call failed: %s",
172                   error->message);
173
174         g_error_free(error);
175
176         return;
177     }
178
179     g_variant_unref(ret);
180
181     *result = CA_STATUS_OK;
182 }
183
184 static void CACentralStopDiscoveryImpl(gpointer proxy, gpointer user_data)
185 {
186     assert(proxy != NULL);
187     assert(user_data != NULL);
188
189     GDBusProxy * const adapter = G_DBUS_PROXY(proxy);
190     CAResult_t * const result  = user_data;
191
192     bool const is_discovering =
193         CACentralGetBooleanProperty(adapter, "Discovering");
194
195     if (!is_discovering)
196     {
197         // Nothing to do.  Avoid invoking a method over D-Bus.
198         *result = CA_STATUS_OK;
199         return;
200     }
201
202     *result = CA_STATUS_FAILED;
203
204
205     GError * error = NULL;
206
207     // Stop discovery sessions.
208     GVariant * const ret =
209         g_dbus_proxy_call_sync(adapter,
210                                "StopDiscovery",
211                                NULL,  // parameters
212                                G_DBUS_CALL_FLAGS_NONE,
213                                -1,    // timeout (default == -1)
214                                NULL,  // cancellable
215                                &error);
216
217     if (ret == NULL)
218     {
219         OIC_LOG_V(ERROR,
220                   TAG,
221                   "StopDiscovery() call failed: %s",
222                   error->message);
223
224         g_error_free(error);
225
226         return;
227     }
228
229     g_variant_unref(ret);
230
231     *result = CA_STATUS_OK;
232 }
233
234 static void CACentralConnectToDevice(gpointer data, gpointer user_data)
235 {
236     assert(data != NULL);
237     assert(user_data != NULL);
238
239     GDBusProxy * const device = G_DBUS_PROXY(data);
240     bool * const connected = user_data;
241
242     if (!CACentralConnect(device))
243     {
244         *connected = false;
245     }
246 }
247
248 /**
249  * Disconnect from all LE peripherals.
250  *
251  * @param[in] context Context containing BlueZ device information.
252  */
253 static void CACentralDisconnect(CALEContext * context)
254 {
255     assert(context != NULL);
256
257     ca_mutex_lock(context->lock);
258
259     for (GList * l = context->devices; l != NULL; l = l->next)
260     {
261         GDBusProxy * const device = G_DBUS_PROXY(l->data);
262
263         bool const is_connected =
264             CACentralGetBooleanProperty(device, "Connected");
265
266         if (is_connected)
267         {
268             /*
269               Asynchronously disconnect.  We don't care about the
270               result so don't bother passing in a callback.
271             */
272             g_dbus_proxy_call(device,
273                               "Disconnect",
274                               NULL,  // parameters
275                               G_DBUS_CALL_FLAGS_NONE,
276                               -1,    // timeout (default == -1),
277                               NULL,  // cancellable
278                               NULL,  // callback
279                               NULL); // user data
280         }
281     }
282
283     ca_mutex_unlock(context->lock);
284 }
285
286 // -----------------------------------------------------------------------
287
288 CAResult_t CACentralStart(CALEContext * context)
289 {
290     assert(context != NULL);
291
292     CAResult_t result = CA_STATUS_FAILED;
293
294     /*
295       Synchronize access to the adapter information using the base
296       context lock since we don't own the adapters.
297      */
298     ca_mutex_lock(context->lock);
299
300     /**
301      * Start discovery on all detected adapters.
302      *
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.
307      */
308     g_list_foreach(context->adapters,
309                    CACentralStartDiscoveryImpl,
310                    &result);
311
312     ca_mutex_unlock(context->lock);
313
314     return result;
315 }
316
317 CAResult_t CACentralStop(CALEContext * context)
318 {
319     assert(context != NULL);
320
321     CAResult_t result = CA_STATUS_FAILED;
322
323     // Stop discovery on all detected adapters.
324     result = CACentralStopDiscovery(context);
325
326     // Disconnect from all adapters.
327     CACentralDisconnect(context);
328
329     /**
330      * @todo Stop notifications on all response characteristics.
331      */
332
333     return result;
334 }
335
336 CAResult_t CACentralStartDiscovery(CALEContext * context)
337 {
338     assert(context != NULL);
339
340     CAResult_t result = CA_STATUS_FAILED;
341
342     /*
343       Synchronize access to the adapter information using the base
344       context lock since we don't own the adapters.
345      */
346     ca_mutex_lock(context->lock);
347
348     // Start discovery on all detected adapters.
349     g_list_foreach(context->adapters,
350                    CACentralStartDiscoveryImpl,
351                    &result);
352
353     ca_mutex_unlock(context->lock);
354
355     return result;
356 }
357
358 CAResult_t CACentralStopDiscovery(CALEContext * context)
359 {
360     assert(context != NULL);
361
362     CAResult_t result = CA_STATUS_FAILED;
363
364     /*
365       Synchronize access to the adapter information using the base
366       context lock since we don't own the adapters.
367      */
368     ca_mutex_lock(context->lock);
369
370     // Stop discovery on all detected adapters.
371     g_list_foreach(context->adapters,
372                    CACentralStopDiscoveryImpl,
373                    &result);
374
375     /**
376      * @todo Stop notifications on all response characteristics.
377      */
378
379     ca_mutex_unlock(context->lock);
380
381     return result;
382 }
383
384 bool CACentralConnect(GDBusProxy * device)
385 {
386     assert(device != NULL);
387
388     /*
389       Check if a connection to the LE peripheral was already
390       established.  If not, establish a connection.
391      */
392     bool const is_connected =
393         CACentralGetBooleanProperty(device, "Connected");
394
395     if (is_connected)
396     {
397         return true;
398     }
399
400     GError * error = NULL;
401
402     // Connect to the discovered LE peripheral asynchronously.
403     GVariant * const ret =
404         g_dbus_proxy_call_sync(device,
405                                "Connect",
406                                NULL,  // parameters
407                                G_DBUS_CALL_FLAGS_NONE,
408                                -1,    // timeout (default == -1),
409                                NULL,  // cancellable
410                                &error);
411
412     if (ret == NULL)
413     {
414         OIC_LOG_V(ERROR,
415                   TAG,
416                   "%s.Connect() call failed: %s",
417                   BLUEZ_DEVICE_INTERFACE,
418                   error->message);
419
420         g_error_free(error);
421
422         return false;
423     }
424
425     g_variant_unref(ret);
426
427     return true;
428 }
429
430 bool CACentralConnectToAll(CALEContext * context)
431 {
432     bool connected = true;
433
434     ca_mutex_lock(context->lock);
435
436     // Connect to the LE peripherals, if we're not already connected.
437     g_list_foreach(context->devices,
438                    CACentralConnectToDevice,
439                    &connected);
440
441     ca_mutex_unlock(context->lock);
442
443     return connected;
444 }