[IOT-771] Reintroduce missing Linux BLE adapter code.
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / bt_le_adapter / linux / client.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 "client.h"
20 #include "recv.h"
21 #include "context.h"
22 #include "bluez.h"
23 #include "utils.h"
24
25 #include "cafragmentation.h"
26 #include "cagattservice.h"
27 #include "logger.h"
28 #include "oic_malloc.h"
29 #include "oic_string.h"
30
31 #include <gio/gio.h>
32
33 #include <strings.h>
34 #include <assert.h>
35
36
37 // Logging tag.
38 static char const TAG[] = "BLE_CLIENT";
39
40 typedef struct _CAGattClientContext
41 {
42     /**
43      * Bluetooth MAC address to GATT characteristic map.
44      *
45      * Hash table that maps Bluetooth MAC address to a OIC Transport
46      * Profile GATT characteristic.  The key is a string containing
47      * the LE peripheral Bluetooth adapter MAC address.   The value is
48      * an interface proxy (@c GDBusProxy) to an
49      * @c org.bluez.GattCharacteristic1 object, i.e. OIC Transport
50      * Profile GATT request characteristic.
51      */
52     GHashTable * characteristic_map;
53
54     /**
55      * Response characteristic object path to Bluetooth MAC address map.
56      *
57      * Hash table that maps OIC Transport Profile GATT response
58      * characteristic D-Bus object path to Bluetooth MAC address.  The
59      * key is the D-Bus object path.  The value is the LE peripheral
60      * Bluetooth adapter MAC address.
61      *
62      * @note This map exists to avoid having to create a hierarchy of
63      *       GLib D-Bus proxies to the client side BlueZ GATT response
64      *       Characteristic, its corresponding GATT Service, and the
65      *       Device object within that Service, along with the
66      *       resulting D-Bus calls, simply so that we obtain the MAC
67      *       address of the remote (peripheral) LE device.  We
68      *       unfortunately need the MAC address since the
69      *       shared/common caleadapter code requires it.
70      */
71     GHashTable * address_map;
72
73     /// Mutex used to synchronize access to context fields.
74     ca_mutex lock;
75
76 } CAGattClientContext;
77
78 static CAGattClientContext g_context = {
79     .lock = NULL
80 };
81
82 // ---------------------------------------------------------------------
83 //                      GATT Response Receive
84 // ---------------------------------------------------------------------
85 static void CAGattClientOnCharacteristicPropertiesChanged(
86     GDBusProxy * characteristic,
87     GVariant * changed_properties,
88     GStrv invalidated_properties,
89     gpointer user_data)
90 {
91     /*
92       This handler is trigged in a GATT client when receiving data
93       sent by a GATT server through a notification, e.g. such as when
94       a GATT server sent a response.
95     */
96
97     (void) invalidated_properties;
98
99     if (g_variant_n_children(changed_properties) < 1)
100     {
101         /*
102           No changed properties, only invalidated ones which we don't
103           care about.
104         */
105         return;
106     }
107
108     CALEContext * const context = user_data;
109     char const * const object_path =
110         g_dbus_proxy_get_object_path(characteristic);
111
112     ca_mutex_lock(g_context.lock);
113
114     char * const address =
115         g_hash_table_lookup(g_context.address_map, object_path);
116
117     /*
118       Address lookup could fail if a property changed on a GATT
119       characteristic that isn't an OIC Transport Profile GATT response
120       characteristic.  This isn't necessarily a problem since it's
121       possible other unrelated GATT charactertistics with changed
122       properties are exposed by BlueZ on the D-Bus system bus.
123     */
124     if (address != NULL)
125     {
126         CAGattRecvInfo info =
127             {
128                 .peer               = address,
129                 .on_packet_received = context->on_client_received_data,
130                 .context            = context
131             };
132
133         GVariant * const value =
134             g_variant_lookup_value(changed_properties, "Value", NULL);
135
136         if (value != NULL)
137         {
138             // GLib maps an octet to a guchar, which is of size 1.
139             gsize length = 0;
140             gconstpointer const data =
141                 g_variant_get_fixed_array(value, &length, 1);
142
143             (void) CAGattRecv(&info, data, length);
144
145             g_variant_unref(value);
146         }
147     }
148
149     ca_mutex_unlock(g_context.lock);
150 }
151
152 // ---------------------------------------------------------------------
153 //                        GATT Client Set-up
154 // ---------------------------------------------------------------------
155 static bool CAGattClientMapInsert(GHashTable * map,
156                                   gpointer key,
157                                   gpointer value)
158 {
159     bool const insert = !g_hash_table_contains(map, key);
160
161     if (insert)
162     {
163         g_hash_table_insert(map, key, value);
164     }
165
166     return insert;
167 }
168
169 static bool CAGattClientSetupCharacteristics(
170     GDBusProxy * service,
171     char const * address,
172     GHashTable * characteristic_map,
173     GHashTable * address_map,
174     CALEContext * context)
175 {
176     bool success = true;
177
178     GVariant * const characteristics_prop =
179         g_dbus_proxy_get_cached_property(service, "Characteristics");
180
181     gsize length = 0;
182     gchar const ** const characteristic_paths =
183         g_variant_get_objv(characteristics_prop, &length);
184
185 #ifdef TB_LOG
186     if (length == 0)
187     {
188         OIC_LOG(ERROR,
189                 TAG,
190                 "Server side OIC GATT Service has no characteristics");
191     }
192 #endif
193
194     /*
195       Create a proxies to the org.bluez.GattCharacteristic1 D-Bus
196       objects that will later be used to send requests and receive
197       responses on the client side.
198     */
199     gchar const * const * const end = characteristic_paths + length;
200     for (gchar const * const * path = characteristic_paths;
201          path != end && success;
202          ++path)
203     {
204         // Find the request characteristic.
205         GError * error = NULL;
206
207         GDBusProxy * const characteristic =
208             g_dbus_proxy_new_sync(context->connection,
209                                   G_DBUS_PROXY_FLAGS_NONE,
210                                   NULL, // GDBusInterfaceInfo
211                                   BLUEZ_NAME,
212                                   *path,
213                                   BLUEZ_GATT_CHARACTERISTIC_INTERFACE,
214                                   NULL, // GCancellable
215                                   &error);
216
217         if (characteristic == NULL)
218         {
219             OIC_LOG_V(ERROR,
220                       TAG,
221                       "Unable to obtain proxy to GATT characteristic: %s",
222                       error->message);
223
224             g_error_free(error);
225
226             success = false;
227
228             break;
229         }
230
231         GVariant * const uuid_prop =
232             g_dbus_proxy_get_cached_property(characteristic, "UUID");
233
234         char const * const uuid =
235             g_variant_get_string(uuid_prop, NULL);
236
237         if (strcasecmp(uuid, CA_GATT_REQUEST_CHRC_UUID) == 0)
238         {
239             char     * const addr = OICStrdup(address);
240             gpointer * const chrc = g_object_ref(characteristic);
241
242             // Map LE (MAC) address to request characteristic.
243             if (!CAGattClientMapInsert(characteristic_map, addr, chrc))
244             {
245                 OIC_LOG_V(WARNING,
246                           TAG,
247                           "Possible duplicate OIC GATT "
248                           "request characteristic proxy detected.");
249
250                 g_object_unref(chrc);
251                 OICFree(addr);
252             }
253         }
254         else if (strcasecmp(uuid, CA_GATT_RESPONSE_CHRC_UUID) == 0)
255         {
256             char * const p    = OICStrdup(*path);
257             char * const addr = OICStrdup(address);
258
259             // Map GATT service D-Bus object path to client address.
260             if (!CAGattClientMapInsert(address_map, p, addr))
261             {
262                 OIC_LOG_V(WARNING,
263                           TAG,
264                           "Unable to register duplicate "
265                           "peripheral MAC address");
266
267                 success = false;
268
269                 OICFree(addr);
270                 OICFree(p);
271             }
272             else
273             {
274                 /*
275                   Detect changes in GATT characteristic properties.
276                   This is only relevant to OIC response
277                   characteristics since only their "Value" property
278                   will ever change.
279                 */
280                 g_signal_connect(
281                     characteristic,
282                     "g-properties-changed",
283                     G_CALLBACK(CAGattClientOnCharacteristicPropertiesChanged),
284                     context);
285
286                 // Enable notifications.
287                 GVariant * const ret =
288                     g_dbus_proxy_call_sync(
289                         characteristic,
290                         "StartNotify",
291                         NULL,  // parameters
292                         G_DBUS_CALL_FLAGS_NONE,
293                         -1,    // timeout (default == -1),
294                         NULL,  // cancellable
295                         &error);
296
297                 if (ret == NULL)
298                 {
299                     OIC_LOG_V(ERROR,
300                               TAG,
301                               "Failed to enable GATT notifications: %s",
302                               error->message);
303
304                     g_error_free(error);
305                     g_hash_table_remove(address_map, address);
306                     success = false;
307                 }
308                 else
309                 {
310                     g_variant_unref(ret);
311                 }
312             }
313         }
314 #ifdef TB_LOG
315         else
316         {
317             OIC_LOG_V(WARNING,
318                       TAG,
319                       "Unrecognized characteristic UUID "
320                       "in OIC GATT service: %s",
321                       uuid);
322         }
323 #endif
324
325         g_variant_unref(uuid_prop);
326         g_object_unref(characteristic);
327     }
328
329     g_free(characteristic_paths);
330     g_variant_unref(characteristics_prop);
331
332     return success;
333 }
334
335 static bool CAGattClientSetupService(
336     GDBusProxy * device,
337     GHashTable * characteristic_map,
338     GHashTable * address_map,
339     GVariant   * services_prop,
340     CALEContext * context)
341 {
342     bool success = true;
343
344     GVariant * const address_prop =
345         g_dbus_proxy_get_cached_property(device, "Address");
346
347     char const * const address =
348         g_variant_get_string(address_prop, NULL);
349
350     /*
351       Create a proxies to the org.bluez.GattService1 D-Bus objects
352       that implement the OIC Transport Profile on the client side.
353
354       The services_prop argument will be non-NULL if changes to the
355       org.bluez.Device1.GattServices property were detected
356       asynchronously through the PropertiesChanged signal.
357     */
358     if (services_prop != NULL)
359     {
360         /*
361           The caller owns the variant so hold on to a reference since
362           we assume ownership in this function.
363         */
364         g_variant_ref(services_prop);
365     }
366     else
367     {
368         // Check if GATT services have already been discovered.
369         services_prop =
370             g_dbus_proxy_get_cached_property(device, "GattServices");
371     }
372
373     gsize length = 0;
374     char const ** const service_paths =
375         services_prop != NULL
376         ? g_variant_get_objv(services_prop, &length)
377         : NULL;
378
379 #ifdef TB_LOG
380     if (length == 0)
381     {
382         // GATT services may not yet have been discovered.
383         OIC_LOG_V(INFO,
384                   TAG,
385                   "GATT services not yet discovered "
386                   "on LE peripheral: %s\n",
387                   address);
388     }
389 #endif
390
391     gchar const * const * const end = service_paths + length;
392     for (gchar const * const * path = service_paths;
393          path != end && success;
394          ++path)
395     {
396         // Find the OIC Transport Profile GATT service.
397         GError * error = NULL;
398
399         GDBusProxy * const service =
400             g_dbus_proxy_new_sync(context->connection,
401                                   G_DBUS_PROXY_FLAGS_NONE,
402                                   NULL, // GDBusInterfaceInfo
403                                   BLUEZ_NAME,
404                                   *path,
405                                   BLUEZ_GATT_SERVICE_INTERFACE,
406                                   NULL, // GCancellable
407                                   &error);
408
409         if (service == NULL)
410         {
411             OIC_LOG_V(ERROR,
412                       TAG,
413                       "Unable to obtain proxy to GATT service: %s",
414                       error->message);
415
416             g_error_free(error);
417
418             success = false;
419
420             break;
421         }
422
423         GVariant * const uuid_prop =
424             g_dbus_proxy_get_cached_property(service, "UUID");
425
426         char const * const uuid =
427             g_variant_get_string(uuid_prop, NULL);
428
429         if (strcasecmp(uuid, CA_GATT_SERVICE_UUID) == 0)
430         {
431             success = CAGattClientSetupCharacteristics(service,
432                                                        address,
433                                                        characteristic_map,
434                                                        address_map,
435                                                        context);
436
437 #ifdef TB_LOG
438             if (!success)
439             {
440                 OIC_LOG_V(ERROR,
441                           TAG,
442                           "Characteristic set up for "
443                           "GATT service at %s failed.",
444                           address);
445             }
446 #endif  // TB_LOG
447         }
448
449         g_variant_unref(uuid_prop);
450         g_object_unref(service);
451     }
452
453     if (services_prop != NULL)
454     {
455         g_variant_unref(services_prop);
456     }
457
458     g_variant_unref(address_prop);
459
460     return success;
461 }
462
463 static void CAGattClientOnDevicePropertiesChanged(
464     GDBusProxy * device,
465     GVariant * changed_properties,
466     GStrv invalidated_properties,
467     gpointer user_data)
468 {
469     /*
470       This handler is trigged in a GATT client when org.bluez.Device1
471       properties have changed.
472     */
473
474     (void) invalidated_properties;
475
476     /*
477       Retrieve the org.bluez.Device1.GattServices property from the
478       changed_properties dictionary parameter (index 1).
479     */
480     GVariant * const services_prop =
481         g_variant_lookup_value(changed_properties, "GattServices", NULL);
482
483     if (services_prop != NULL)
484     {
485         CALEContext * const context = user_data;
486
487         ca_mutex_lock(g_context.lock);
488
489         CAGattClientSetupService(device,
490                                  g_context.characteristic_map,
491                                  g_context.address_map,
492                                  services_prop,
493                                  context);
494
495         ca_mutex_unlock(g_context.lock);
496
497         g_variant_unref(services_prop);
498     }
499 }
500
501 CAResult_t CAGattClientInitialize(CALEContext * context)
502 {
503     g_context.lock = ca_mutex_new();
504
505     /*
506       Map Bluetooth MAC address to OIC Transport Profile
507       request characteristics.
508     */
509     GHashTable * const characteristic_map =
510         g_hash_table_new_full(g_str_hash,
511                               g_str_equal,
512                               OICFree,
513                               g_object_unref);
514
515     /*
516       Map OIC Transport Profile response characteristic D-Bus object
517       path to Bluetooth MAC address.
518     */
519     GHashTable * const address_map =
520         g_hash_table_new_full(g_str_hash,
521                               g_str_equal,
522                               OICFree,
523                               OICFree);
524
525     ca_mutex_lock(context->lock);
526
527     for (GList * l = context->devices; l != NULL; l = l->next)
528     {
529         GDBusProxy * const device = G_DBUS_PROXY(l->data);
530
531         /*
532           Detect changes in BlueZ Device properties.  This is
533           predominantly used to detect GATT services that were
534           discovered asynchronously.
535         */
536         g_signal_connect(
537             device,
538             "g-properties-changed",
539             G_CALLBACK(CAGattClientOnDevicePropertiesChanged),
540             context);
541
542         CAGattClientSetupService(device,
543                                  characteristic_map,
544                                  address_map,
545                                  NULL,
546                                  context);
547     }
548
549     ca_mutex_unlock(context->lock);
550
551     ca_mutex_lock(g_context.lock);
552
553     g_context.characteristic_map = characteristic_map;
554     g_context.address_map = address_map;
555
556     ca_mutex_unlock(g_context.lock);
557
558     return CA_STATUS_OK;
559 }
560
561 void CAGattClientDestroy()
562 {
563     if (g_context.lock == NULL)
564     {
565         return;  // Initialization did not complete.
566     }
567
568     ca_mutex_lock(g_context.lock);
569
570     if (g_context.characteristic_map != NULL)
571     {
572         g_hash_table_unref(g_context.characteristic_map);
573         g_context.characteristic_map = NULL;
574     }
575
576     if (g_context.address_map != NULL)
577     {
578         g_hash_table_unref(g_context.address_map);
579         g_context.address_map = NULL;
580     }
581
582     ca_mutex_unlock(g_context.lock);
583
584     ca_mutex_free(g_context.lock);
585     g_context.lock = NULL;
586
587     /*
588       We don't explicitly stop notifications on the response
589       characteristic since they should be stopped upon server side
590       disconnection.
591      */
592 }
593
594 // ---------------------------------------------------------------------
595 //                      GATT Request Data Send
596 // ---------------------------------------------------------------------
597
598 static CAResult_t CAGattClientSendDataImpl(GDBusProxy * characteristic,
599                                            uint8_t const * data,
600                                            size_t length,
601                                            CALEContext * context)
602 {
603     assert(characteristic != NULL);
604     assert(data != NULL);
605     assert(context != NULL);
606
607     GVariant * const value =
608         g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
609                                   data,
610                                   length,
611                                   1);  // sizeof(data[0]) == 1
612
613     /*
614       WriteValue() expects a byte array but it must be packed into a
615       tuple for the actual call through the proxy.
616     */
617     GVariant * const value_parameter = g_variant_new("(@ay)", value);
618
619     GError * error = NULL;
620
621     GVariant * const ret =
622         g_dbus_proxy_call_sync(characteristic,
623                                "WriteValue",
624                                value_parameter,  // parameters
625                                G_DBUS_CALL_FLAGS_NONE,
626                                -1,    // timeout (default == -1),
627                                NULL,  // cancellable
628                                &error);
629
630     if (ret == NULL)
631     {
632         OIC_LOG_V(ERROR,
633                   TAG,
634                   "[%p] WriteValue() call failed: %s",
635                   characteristic,
636                   error->message);
637
638         g_error_free(error);
639
640         ca_mutex_lock(context->lock);
641
642         if (context->on_client_error != NULL)
643         {
644             /*
645               At this point endpoint and send data information is
646               available.
647             */
648             context->on_client_error(NULL,   // endpoint
649                                      data,
650                                      length,
651                                      CA_STATUS_FAILED);
652         }
653
654         ca_mutex_unlock(context->lock);
655
656         return CA_STATUS_FAILED;
657     }
658
659     g_variant_unref(ret);
660
661     return CA_STATUS_OK;
662 }
663
664 CAResult_t CAGattClientSendData(char const * address,
665                                 uint8_t const * data,
666                                 size_t length,
667                                 CALEContext * context)
668 {
669     assert(context != NULL);
670
671     CAResult_t result = CA_STATUS_FAILED;
672
673     ca_mutex_lock(g_context.lock);
674
675     GDBusProxy * const characteristic =
676         G_DBUS_PROXY(
677             g_hash_table_lookup(g_context.characteristic_map,
678                                 address));
679
680     if (characteristic == NULL)
681     {
682         /*
683           GATT request characteristic corresponding to given address
684           was not found.
685         */
686
687         return result;
688     }
689
690     result = CAGattClientSendDataImpl(characteristic,
691                                       data,
692                                       length,
693                                       context);
694
695     ca_mutex_unlock(g_context.lock);
696
697     return result;
698 }
699
700 CAResult_t CAGattClientSendDataToAll(uint8_t const * data,
701                                      size_t length,
702                                      CALEContext * context)
703 {
704     assert(context != NULL);
705
706     CAResult_t result = CA_STATUS_FAILED;
707
708     ca_mutex_lock(g_context.lock);
709
710     if (g_context.characteristic_map == NULL)
711     {
712         // Remote OIC GATT service was not found prior to getting here.
713         ca_mutex_unlock(g_context.lock);
714         return result;
715     }
716
717     GHashTableIter iter;
718     g_hash_table_iter_init(&iter, g_context.characteristic_map);
719
720     gpointer characteristic;  // Value
721
722     /**
723      * @todo Will content of this hash table be potentially changed by
724      *       another thread during iteration?
725      */
726     while(g_hash_table_iter_next(&iter,
727                                  NULL,  // Key - unused
728                                  &characteristic))
729     {
730         result = CAGattClientSendDataImpl(G_DBUS_PROXY(characteristic),
731                                           data,
732                                           length,
733                                           context);
734
735         if (result != CA_STATUS_OK)
736         {
737             break;
738         }
739     }
740
741     ca_mutex_unlock(g_context.lock);
742
743     return result;
744 }