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