Imported Upstream version 1.0.0
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / bt_le_adapter / linux / characteristic.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 "characteristic.h"
20 #include "service.h"
21 #include "gatt_dbus.h"
22 #include "utils.h"
23 #include "bluez.h"
24 #include "server.h"
25
26 #include "oic_malloc.h"
27 #include "oic_string.h"
28 #include "logger.h"
29 #include "cagattservice.h"
30 #include "caremotehandler.h"
31
32 #include <inttypes.h>
33 #include <string.h>
34 #include <assert.h>
35
36
37 // Logging tag.
38 static char const TAG[] = "BLE_CHARACTERISTIC";
39
40 // ---------------------------------------------------------------------
41 //                      GATT Request Handling
42 // ---------------------------------------------------------------------
43 /**
44  * Handle @c org.bluez.GattCharacterstic1.WriteValue() method call.
45  *
46  * This handler is triggered when the
47  * @c org.bluez.GattCharacterstic1.WriteValue() method is called by a
48  * client on the BlueZ-based OIC GATT request characteristic.  In
49  * particular, IoTivity request data is sent by the GATT client to the
50  * GATT server through this call.  The server will retrieve the data
51  * that was sent from the @a value argument.  Reassembly of any
52  * request data fragments will begin here.
53  *
54  * @param[in] object     @c org.bluez.GattCharacteristic1 skeleton
55  *                       object associated with this method call.
56  * @param[in] invocation D-Bus method invocation related object used
57  *                       when sending results/errors asynchronously.
58  * @param[in] value      The @c GVariant containing the byte array
59  *                       (ay) with th request data inside.
60  * @param[in] user_data  Pointer to request
61  *                       @c CAGattCharacteristic object.
62  *
63  * @return @c TRUE to indicate that
64  *         @c org.bluez.Characteristic.WriteValue() method is
65  *         implemented.
66  */
67 static gboolean CAGattCharacteristicHandleWriteValue(
68     GattCharacteristic1 * object,
69     GDBusMethodInvocation * invocation,
70     GVariant * value,
71     gpointer user_data)
72 {
73     /*
74       This method is only trigged in a GATT server when receiving
75       data sent by a GATT client, i.e. the client wrote a value to
76       the server, and the server is handling that write request.
77     */
78
79     // GLib maps an octet to a guchar, which is of size 1.
80     gsize len = 0;
81     gconstpointer const data =
82         g_variant_get_fixed_array(value, &len, 1);
83
84     CAGattCharacteristic * const c = user_data;
85
86     if (CAGattRecv(&c->recv_info, data, (uint32_t) len))
87     {
88         gatt_characteristic1_complete_write_value(object, invocation);
89     }
90     else
91     {
92         g_dbus_method_invocation_return_dbus_error(
93             invocation,
94             "org.bluez.Error.Failed",
95             "Error when handling GATT request data fragment");
96     }
97
98     return TRUE;
99 }
100
101 // ---------------------------------------------------------------------
102 //                      GATT Response Handling
103 // ---------------------------------------------------------------------
104 /**
105  * Make the peer address corresponding to the given GATT
106  * characteristic.
107  *
108  * @param[in] c Information about GATT characteristic for which the
109  *              peer (client) @c CAEndpoint_t object is being
110  *              created.
111  *
112  * @return @c String containing an encoded address associated with the
113  *         peer connected to the peripheral on which the characteristic
114  *         implementation resides, or @c NULL on error.
115  */
116 static char * CAGattCharacteristicMakePeerAddress(
117     CAGattCharacteristic * c)
118 {
119     assert(c != NULL);
120
121     /*
122       Length of stringified pointer in hexadecimal format, plus one
123       for null terminator.
124     */
125     static size_t const PSEUDO_ADDR_LEN = sizeof(intptr_t) / 4 + 1;
126
127     assert(MAX_ADDR_STR_SIZE_CA > PSEUDO_ADDR_LEN);
128
129     /*
130       Since there is no direct way to obtain the client endpoint
131       associated with the GATT characterstics on the server side,
132       embed a stringified pointer to the response charactertistic of
133       the form "&ABCDEF01" is the CAEndpoint_t instead.  This works
134       since:
135           1) only one LE central is ever connected to an LE peripheral
136           2) the CA layer doesn't directly interpret the address
137      */
138     char * const addr = OICMalloc(PSEUDO_ADDR_LEN);
139     int const count = snprintf(addr,
140                                PSEUDO_ADDR_LEN,
141                                "&%" PRIxPTR,
142                                (uintptr_t) c);
143
144     if (count >= (int) PSEUDO_ADDR_LEN)
145     {
146         OIC_LOG(ERROR,
147                 TAG,
148                 "Error creating peer address on server side.");
149
150         return NULL;
151     }
152
153     return addr;
154 }
155
156 /**
157  * Handle @c org.bluez.GattCharacterstic1.StartNotify() method call.
158  *
159  * This handler is triggered when the
160  * @c org.bluez.GattCharacterstic1.StartNotify() method is called by a
161  * client on the BlueZ-based OIC GATT response characteristic.  It
162  * sets the @c org.bluez.GattCharacterstic1.Notifying property to
163  * @c TRUE, and enables responses from the server.
164  *
165  * @note This handler is not available in the OIC GATT request
166  *       characteristic implementation.
167  *
168  * @param[in] object     @c org.bluez.GattCharacteristic1 skeleton
169  *                       object associated with this method call.
170  * @param[in] invocation D-Bus method invocation related object used
171  *                       when sending results/errors asynchronously.
172  * @param[in] user_data  Pointer to the response
173  *                       @c CAGattCharacteristic object.
174  *
175  * @return @c TRUE to indicate that
176  *         @c org.bluez.Characteristic.StartNotify() method is
177  *         implemented.
178  */
179 static gboolean CAGattCharacteristicHandleStartNotify(
180     GattCharacteristic1 * object,
181     GDBusMethodInvocation * invocation,
182     gpointer user_data)
183 {
184     /**
185      * Only allow the client to start notifications once.
186      *
187      * @todo Does BlueZ already prevent redundant calls to
188      *       @c org.bluez.GattCharacteristic1.StartNotify()?
189      */
190     if (gatt_characteristic1_get_notifying(object))
191     {
192         g_dbus_method_invocation_return_dbus_error(
193             invocation,
194             "org.bluez.Error.Failed",
195             "Notifications are already enabled.");
196
197         return TRUE;
198     }
199
200     // Retrieve the response characteristic information.
201     CAGattCharacteristic * const characteristic = user_data;
202
203     char * const peer =
204         CAGattCharacteristicMakePeerAddress(characteristic);
205
206     if (peer == NULL)
207     {
208         g_dbus_method_invocation_return_dbus_error(
209             invocation,
210             "org.bluez.Error.Failed",
211             "Error creating peer endpoint information");
212
213         return TRUE;
214     }
215
216     /*
217       Create an entry in the endpoint-to-characteristic map so that
218       responses may be sent to the GATT client through the OIC GATT
219       response characteristic through this BLE adapter's
220       CAAdapterSendUnicastData() implementation.
221      */
222     CALEContext * const context = characteristic->context;
223
224     ca_mutex_lock(context->lock);
225
226 #if GLIB_CHECK_VERSION(2,40,0)
227     /*
228       GLib hash table functions started returning a boolean result in
229        version 2.40.x.
230     */
231     bool const inserted =
232 #endif
233         g_hash_table_insert(context->characteristic_map,
234                             peer,
235                             characteristic);
236
237     ca_mutex_unlock(context->lock);
238
239 #if GLIB_CHECK_VERSION(2,40,0)
240     if (!inserted)
241     {
242         g_dbus_method_invocation_return_dbus_error(
243             invocation,
244             "org.bluez.Error.Failed",
245             "Unable to set response endpoint.");
246
247         OICFree(peer);
248
249         return TRUE;
250     }
251 #endif
252
253     /**
254      * @todo Do we need to explicitly emit the @c GObject @c notify or
255      *       @c org.freedesktop.Dbus.Properties.PropertiesChanged
256      *       signal here?
257      */
258     gatt_characteristic1_set_notifying(object, TRUE);
259
260     /*
261       Set the client endpoint field in the request characteristic so
262       that it may pass the appropriate endpoint object up the stack
263       through the CA request/response callback once a request has been
264       completely received and reassembled.
265     */
266     CAGattRecvInfo * const recv_info =
267         &characteristic->service->request_characteristic.recv_info;
268
269     recv_info->peer = peer;
270
271     ca_mutex_lock(context->lock);
272     recv_info->on_packet_received = context->on_server_received_data;
273     recv_info->context            = context;
274     ca_mutex_unlock(context->lock);
275
276     gatt_characteristic1_complete_start_notify(object, invocation);
277
278     return TRUE;
279 }
280
281 /**
282  * Handle @c org.bluez.GattCharacterstic1.StopNotify() method call.
283  *
284  * This handler is triggered when the
285  * @c org.bluez.GattCharacterstic1.StopNotify() method is called by a
286  * client on the BlueZ-based OIC GATT response characteristic.  It
287  * sets the @c org.bluez.GattCharacterstic1.Notifying property to
288  * @c FALSE, and disables responses from the server.
289  *
290  * @param[in] object     @c org.bluez.GattCharacteristic1 skeleton
291  *                       object associated with this method call.
292  * @param[in] invocation D-Bus method invocation related object used
293  *                       when sending results/errors asynchronously.
294  * @param[in] user_data  Pointer to the response
295  *                       @c CAGattCharacteristic object.
296  *
297  * @return @c TRUE to indicate that
298  *         @c org.bluez.Characteristic.StopNotify() method is
299  *         implemented.
300  */
301 static gboolean CAGattCharacteristicHandleStopNotify(
302     GattCharacteristic1 * object,
303     GDBusMethodInvocation * invocation,
304     gpointer user_data)
305 {
306     assert(user_data != NULL);
307
308     /**
309      * @todo Does BlueZ already prevent redundant calls to
310      *       @c org.bluez.GattCharacteristic1.StopNotify()?
311      */
312     if (!gatt_characteristic1_get_notifying(object))
313     {
314         g_dbus_method_invocation_return_dbus_error(
315             invocation,
316             "org.bluez.Error.Failed",
317             "Notifications were not previously enabled.");
318
319         return TRUE;
320     }
321
322     CAGattCharacteristic * const characteristic = user_data;
323
324     // Clear the client endpoint from the request characteristic.
325     CAGattRecvInfo * const recv_info =
326         &characteristic->service->request_characteristic.recv_info;
327
328     /*
329       Remove the appropriate entry from the endpoint-to-characteristic
330       map so that attempts to send a response through it will fail.
331     */
332     CALEContext * const context = characteristic->context;
333     ca_mutex_lock(context->lock);
334
335     bool const removed =
336         g_hash_table_remove(context->characteristic_map, recv_info->peer);
337
338     ca_mutex_unlock(context->lock);
339
340     CAGattRecvInfoDestroy(recv_info);
341
342     /**
343      * @todo Do we need to explicitly emit the @c GObject @c notify or
344      *       @c org.freedesktop.Dbus.Properties.PropertiesChanged
345      *       signal here?
346      */
347     gatt_characteristic1_set_notifying(object, FALSE);
348
349     if (removed)
350     {
351         gatt_characteristic1_complete_stop_notify(object, invocation);
352     }
353     else
354     {
355         g_dbus_method_invocation_return_dbus_error(
356             invocation,
357             "org.bluez.Error.Failed",
358             "Error removing peer address information");
359     }
360
361     return TRUE;
362 }
363
364 // ---------------------------------------------------------------------
365 //                   GATT Characteristic Lifecyle
366 // ---------------------------------------------------------------------
367
368 /**
369  * Initialize GATT characteristic fields.
370  *
371  * This function initializes the @c CAGattCharacteristic object fields.
372  *
373  * @param[out] c                   GATT characteristic information to
374  *                                 be initialized.
375  * @param[in]  context             Object containing the D-Bus
376  *                                 connection to the bus on which the
377  *                                 characteristic will be exported, as
378  *                                 well as the list of connected
379  *                                 devices.
380  * @param[in]  s                   Information about GATT service
381  *                                 to which the characteristic
382  *                                 belongs.
383  * @param[in]  characteristic_path @c GattCharacteristic1 object
384  *                                 path.
385  * @param[in]  uuid                GATT characteristic UUID.
386  * @param[in]  flag                GATT characteristic property flag,
387  *                                 i.e. @c "write-without-response"
388  *                                 for the request characteristic, and
389  *                                 @c "notify" for the response
390  *                                 characteristic.
391  *
392  * @note This function does not allocate the @a characteristic object
393  *       itself.  The caller is responsible for allocating that
394  *       memory.
395  *
396  * @todo Too many parameters.  Perhaps pass in a pointer to a struct
397  *       instead.
398  */
399 static bool CAGattCharacteristicInitialize(
400     CAGattCharacteristic * c,
401     CALEContext * context,
402     CAGattService * s,
403     char const * characteristic_path,
404     char const * uuid,
405     char const * flag)
406 {
407     // Path of the form /org/iotivity/gatt/hci0/service0/char0.
408     c->object_path =
409         g_strdup_printf("%s/%s", s->object_path, characteristic_path);
410
411     assert(g_variant_is_object_path(c->object_path));
412
413     c->context = context;
414     c->service = s;
415
416     c->characteristic = gatt_characteristic1_skeleton_new();
417
418     gatt_characteristic1_set_uuid(c->characteristic, uuid);
419     gatt_characteristic1_set_service(c->characteristic, s->object_path);
420     gatt_characteristic1_set_notifying(c->characteristic, FALSE);
421
422     char const * flags[] = { flag, NULL };
423     gatt_characteristic1_set_flags(c->characteristic, flags);
424
425     CAGattRecvInfoInitialize(&c->recv_info);
426
427     // Export the characteristic interface on the bus.
428     GError * error = NULL;
429     if (!g_dbus_interface_skeleton_export(
430             G_DBUS_INTERFACE_SKELETON(c->characteristic),
431             context->connection,
432             c->object_path,
433             &error))
434     {
435         CAGattCharacteristicDestroy(c);
436
437         OIC_LOG_V(ERROR,
438                   TAG,
439                   "Unable to export D-Bus GATT characteristic "
440                   "interface: %s",
441                   error->message);
442
443         g_error_free(error);
444
445         return false;
446     }
447
448     return true;
449 }
450
451 // ------------------------------------------------------------
452
453 bool CAGattRequestCharacteristicInitialize(struct CAGattService * s,
454                                            CALEContext * context)
455 {
456     CAGattCharacteristic * const c = &s->request_characteristic;
457
458     if (!CAGattCharacteristicInitialize(c,
459                                         context,
460                                         s,
461                                         CA_GATT_REQUEST_CHRC_PATH,
462                                         CA_GATT_REQUEST_CHRC_UUID,
463                                         "write-without-response"))
464     {
465         return false;
466     }
467
468     if (!CAGattRequestDescriptorInitialize(s, context->connection))
469     {
470         CAGattCharacteristicDestroy(c);
471         return false;
472     }
473
474     /*
475       The descriptor object path is not fixed at compile-time.
476       Retrieve the object path that was set at run-time.
477     */
478     char const * descriptor_paths[] = {
479         c->descriptor.object_path,
480         NULL
481     };
482
483     gatt_characteristic1_set_descriptors(c->characteristic,
484                                          descriptor_paths);
485
486     // The request characteristic only handles writes.
487     g_signal_connect(c->characteristic,
488                      "handle-write-value",
489                      G_CALLBACK(CAGattCharacteristicHandleWriteValue),
490                      c);
491
492     return true;
493 }
494
495 // ------------------------------------------------------------
496
497 bool CAGattResponseCharacteristicInitialize(struct CAGattService * s,
498                                             CALEContext * context)
499 {
500     CAGattCharacteristic * const c = &s->response_characteristic;
501
502     if (!CAGattCharacteristicInitialize(c,
503                                         context,
504                                         s,
505                                         CA_GATT_RESPONSE_CHRC_PATH,
506                                         CA_GATT_RESPONSE_CHRC_UUID,
507                                         "notify"))
508     {
509         return false;
510     }
511
512     c->service = s;
513
514     if (!CAGattResponseDescriptorInitialize(s, context->connection))
515     {
516         CAGattCharacteristicDestroy(c);
517         return false;
518     }
519
520     /*
521       The descriptor object path is not fixed at compile-time.
522       Retrieve the object path that was set at run-time.
523
524       Note that we don't explicitly add the Client Characteristic
525       Configuration Descriptor for the response characteristic since
526       that is done by BlueZ when the "notify" property is set.
527       Furthermore, a client requests notifications by calling the
528       org.bluez.GattCharacteristic1.StartNotify() method.
529       Consequently, there is no need for the client to explicitly
530       enable notifications by writing to the client characteristic
531       configuration descriptor.
532     */
533     char const * descriptor_paths[] = {
534         c->descriptor.object_path,
535         NULL
536     };
537
538     gatt_characteristic1_set_descriptors(c->characteristic,
539                                          descriptor_paths);
540
541     // The response characteristic only handles notifications.
542     g_signal_connect(
543         c->characteristic,
544         "handle-start-notify",
545         G_CALLBACK(CAGattCharacteristicHandleStartNotify),
546         c);
547
548     g_signal_connect(
549         c->characteristic,
550         "handle-stop-notify",
551         G_CALLBACK(CAGattCharacteristicHandleStopNotify),
552         c);
553
554     return true;
555 }
556
557 void CAGattCharacteristicDestroy(CAGattCharacteristic * c)
558 {
559     assert(c != NULL);  // As designed, c is always non-NULL.
560
561     CAGattRecvInfoDestroy(&c->recv_info);
562
563     CAGattDescriptorDestroy(&c->descriptor);
564
565     g_clear_object(&c->characteristic);
566
567     g_free(c->object_path);
568     c->object_path = NULL;
569
570     c->service = NULL;
571     c->context = NULL;
572 }
573
574 // ---------------------------------------------------------------------
575 //                  GATT Characteristic Properties
576 // ---------------------------------------------------------------------
577
578
579 GVariant * CAGattCharacteristicGetProperties(
580     GattCharacteristic1 * characteristic)
581 {
582     /**
583      * Create a variant containing the @c GattCharacteristic1
584      * properties, of the form @c a{sa{sv}}.
585      *
586      * @note We don't care about the "Value" property here since it is
587      *       automatically made available by BlueZ on the client
588      *       side.
589      *
590      * @todo Should we care about "Notifying" property here?
591      */
592
593     /*
594       Populate the property table, and create the variant to be
595       embedded in the results of the
596       org.freedesktop.Dbus.ObjectManager.GetManagedObjects() method
597       call.
598     */
599     CADBusSkeletonProperty const properties[] = {
600         { "UUID",
601           g_variant_new_string(
602               gatt_characteristic1_get_uuid(characteristic)) },
603         { "Service",
604           g_variant_new_object_path(
605               gatt_characteristic1_get_service(characteristic)) },
606         { "Flags",
607           g_variant_new_strv(
608               gatt_characteristic1_get_flags(characteristic),
609               -1) },
610         { "Descriptors",
611           g_variant_new_objv(
612               gatt_characteristic1_get_descriptors(characteristic),
613               -1) }
614     };
615
616     return
617         CAMakePropertyDictionary(
618             BLUEZ_GATT_CHARACTERISTIC_INTERFACE,
619             properties,
620             sizeof(properties) / sizeof(properties[0]));
621 }