replace : iotivity -> iotivity-sec
[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 "logger.h"
27 #include "cagattservice.h"
28 #include "caremotehandler.h"
29
30 #include <assert.h>
31
32
33 // Logging tag.
34 static char const TAG[] = "BLE_CHARACTERISTIC";
35
36 // ---------------------------------------------------------------------
37 //                      GATT Request Handling
38 // ---------------------------------------------------------------------
39 /**
40  * Handle @c org.bluez.GattCharacterstic1.WriteValue() method call.
41  *
42  * This handler is triggered when the
43  * @c org.bluez.GattCharacterstic1.WriteValue() method is called by a
44  * client on the BlueZ-based OIC GATT request characteristic.  In
45  * particular, IoTivity request data is sent by the GATT client to the
46  * GATT server through this call.  The server will retrieve the data
47  * that was sent from the @a value argument.  Reassembly of any
48  * request data fragments will begin here.
49  *
50  * @param[in] object     @c org.bluez.GattCharacteristic1 skeleton
51  *                       object associated with this method call.
52  * @param[in] invocation D-Bus method invocation related object used
53  *                       when sending results/errors asynchronously.
54  * @param[in] value      The @c GVariant containing the byte array
55  *                       (ay) with th request data inside.
56  * @param[in] user_data  Pointer to request
57  *                       @c CAGattCharacteristic object.
58  *
59  * @return @c TRUE to indicate that
60  *         @c org.bluez.Characteristic.WriteValue() method is
61  *         implemented.
62  */
63 static gboolean CAGattCharacteristicHandleWriteValue(
64     GattCharacteristic1 * object,
65     GDBusMethodInvocation * invocation,
66     GVariant * value,
67     gpointer user_data)
68 {
69     /*
70       This method is only trigged in a GATT server when receiving
71       data sent by a GATT client, i.e. the client wrote a value to
72       the server, and the server is handling that write request.
73     */
74
75     // GLib maps an octet to a guchar, which is of size 1.
76     gsize len = 0;
77     gconstpointer const data =
78         g_variant_get_fixed_array(value, &len, 1);
79
80     CAGattCharacteristic * const c = user_data;
81
82     if (CAGattRecv(&c->recv_info, data, (uint32_t) len))
83     {
84         gatt_characteristic1_complete_write_value(object, invocation);
85     }
86     else
87     {
88         g_dbus_method_invocation_return_dbus_error(
89             invocation,
90             "org.bluez.Error.Failed",
91             "Error when handling GATT request data fragment");
92     }
93
94     return TRUE;
95 }
96
97 // ---------------------------------------------------------------------
98 //                      GATT Response Handling
99 // ---------------------------------------------------------------------
100 /**
101  * Handle @c org.bluez.GattCharacterstic1.StartNotify() method call.
102  *
103  * This handler is triggered when the
104  * @c org.bluez.GattCharacterstic1.StartNotify() method is called by a
105  * client on the BlueZ-based OIC GATT response characteristic.  It
106  * sets the @c org.bluez.GattCharacterstic1.Notifying property to
107  * @c TRUE, and enables responses from the server.
108  *
109  * @note This handler is not available in the OIC GATT request
110  *       characteristic implementation.
111  *
112  * @param[in] object     @c org.bluez.GattCharacteristic1 skeleton
113  *                       object associated with this method call.
114  * @param[in] invocation D-Bus method invocation related object used
115  *                       when sending results/errors asynchronously.
116  * @param[in] user_data  Pointer to the response
117  *                       @c CAGattCharacteristic object.
118  *
119  * @return @c TRUE to indicate that
120  *         @c org.bluez.Characteristic.StartNotify() method is
121  *         implemented.
122  */
123 static gboolean CAGattCharacteristicHandleStartNotify(
124     GattCharacteristic1 * object,
125     GDBusMethodInvocation * invocation,
126     gpointer user_data)
127 {
128     (void) user_data;
129
130     /**
131      * Only allow the client to start notifications once.
132      *
133      * @todo Does BlueZ already prevent redundant calls to
134      *       @c org.bluez.GattCharacteristic1.StartNotify()?
135      */
136     if (gatt_characteristic1_get_notifying(object))
137     {
138         g_dbus_method_invocation_return_dbus_error(
139             invocation,
140             "org.bluez.Error.Failed",
141             "Notifications are already enabled.");
142
143         return TRUE;
144     }
145
146     /**
147      * @todo Do we need to explicitly emit the @c GObject @c notify or
148      *       @c org.freedesktop.Dbus.Properties.PropertiesChanged
149      *       signal here?
150      */
151     gatt_characteristic1_set_notifying(object, TRUE);
152     gatt_characteristic1_complete_start_notify(object, invocation);
153
154     return TRUE;
155 }
156
157 /**
158  * Handle @c org.bluez.GattCharacterstic1.StopNotify() method call.
159  *
160  * This handler is triggered when the
161  * @c org.bluez.GattCharacterstic1.StopNotify() 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 FALSE, and disables responses from the server.
165  *
166  * @param[in] object     @c org.bluez.GattCharacteristic1 skeleton
167  *                       object associated with this method call.
168  * @param[in] invocation D-Bus method invocation related object used
169  *                       when sending results/errors asynchronously.
170  * @param[in] user_data  Pointer to the response
171  *                       @c CAGattCharacteristic object.
172  *
173  * @return @c TRUE to indicate that
174  *         @c org.bluez.Characteristic.StopNotify() method is
175  *         implemented.
176  */
177 static gboolean CAGattCharacteristicHandleStopNotify(
178     GattCharacteristic1 * object,
179     GDBusMethodInvocation * invocation,
180     gpointer user_data)
181 {
182     (void) user_data;
183
184     /**
185      * @todo Does BlueZ already prevent redundant calls to
186      *       @c org.bluez.GattCharacteristic1.StopNotify()?
187      */
188     if (!gatt_characteristic1_get_notifying(object))
189     {
190         g_dbus_method_invocation_return_dbus_error(
191             invocation,
192             "org.bluez.Error.Failed",
193             "Notifications were not previously enabled.");
194
195         return TRUE;
196     }
197
198     /**
199      * @todo Do we need to explicitly emit the @c GObject @c notify or
200      *       @c org.freedesktop.Dbus.Properties.PropertiesChanged
201      *       signal here?
202      */
203     gatt_characteristic1_set_notifying(object, FALSE);
204     gatt_characteristic1_complete_stop_notify(object, invocation);
205
206     return TRUE;
207 }
208
209 // ---------------------------------------------------------------------
210 //                   GATT Characteristic Lifecyle
211 // ---------------------------------------------------------------------
212
213 /**
214  * Initialize GATT characteristic fields.
215  *
216  * This function initializes the @c CAGattCharacteristic object fields.
217  *
218  * @param[out] c                   GATT characteristic information to
219  *                                 be initialized.
220  * @param[in]  context             Object containing the D-Bus
221  *                                 connection to the bus on which the
222  *                                 characteristic will be exported, as
223  *                                 well as the list of connected
224  *                                 devices.
225  * @param[in]  s                   Information about GATT service
226  *                                 to which the characteristic
227  *                                 belongs.
228  * @param[in]  characteristic_path @c GattCharacteristic1 object
229  *                                 path.
230  * @param[in]  uuid                GATT characteristic UUID.
231  * @param[in]  flag                GATT characteristic property flag,
232  *                                 i.e. @c "write-without-response"
233  *                                 for the request characteristic, and
234  *                                 @c "notify" for the response
235  *                                 characteristic.
236  *
237  * @note This function does not allocate the @a characteristic object
238  *       itself.  The caller is responsible for allocating that
239  *       memory.
240  *
241  * @todo Too many parameters.  Perhaps pass in a pointer to a struct
242  *       instead.
243  */
244 static bool CAGattCharacteristicInitialize(
245     CAGattCharacteristic * c,
246     CALEContext * context,
247     CAGattService * s,
248     char const * characteristic_path,
249     char const * uuid,
250     char const * flag)
251 {
252     // Path of the form /org/iotivity/gatt/hci0/service0/char0.
253     c->object_path =
254         g_strdup_printf("%s/%s", s->object_path, characteristic_path);
255
256     assert(g_variant_is_object_path(c->object_path));
257
258     c->context = context;
259     c->service = s;
260
261     c->characteristic = gatt_characteristic1_skeleton_new();
262
263     gatt_characteristic1_set_uuid(c->characteristic, uuid);
264     gatt_characteristic1_set_service(c->characteristic, s->object_path);
265     gatt_characteristic1_set_notifying(c->characteristic, FALSE);
266
267     char const * const flags[] = { flag, NULL };
268     gatt_characteristic1_set_flags(c->characteristic, flags);
269
270     CAGattRecvInfoInitialize(&c->recv_info);
271
272     // Export the characteristic interface on the bus.
273     GError * error = NULL;
274     if (!g_dbus_interface_skeleton_export(
275             G_DBUS_INTERFACE_SKELETON(c->characteristic),
276             context->connection,
277             c->object_path,
278             &error))
279     {
280         CAGattCharacteristicDestroy(c);
281
282         OIC_LOG_V(ERROR,
283                   TAG,
284                   "Unable to export D-Bus GATT characteristic "
285                   "interface: %s",
286                   error->message);
287
288         g_error_free(error);
289
290         return false;
291     }
292
293     return true;
294 }
295
296 // ------------------------------------------------------------
297
298 bool CAGattRequestCharacteristicInitialize(struct CAGattService * s,
299                                            CALEContext * context)
300 {
301     CAGattCharacteristic * const c = &s->request_characteristic;
302
303     if (!CAGattCharacteristicInitialize(c,
304                                         context,
305                                         s,
306                                         CA_GATT_REQUEST_CHRC_PATH,
307                                         CA_GATT_REQUEST_CHRC_UUID,
308                                         "write-without-response"))
309     {
310         return false;
311     }
312
313     if (!CAGattRequestDescriptorInitialize(s, context->connection))
314     {
315         CAGattCharacteristicDestroy(c);
316         return false;
317     }
318
319     /*
320       The descriptor object path is not fixed at compile-time.
321       Retrieve the object path that was set at run-time.
322     */
323     char const * const descriptor_paths[] = {
324         c->descriptor.object_path,
325         NULL
326     };
327
328     gatt_characteristic1_set_descriptors(c->characteristic,
329                                          descriptor_paths);
330
331     char * const peer = CAGattServiceMakePeerAddress(s);
332
333     if (peer == NULL)
334     {
335         CAGattCharacteristicDestroy(c);
336         return false;
337     }
338
339     oc_mutex_lock(context->lock);
340     c->recv_info.on_packet_received = context->on_server_received_data;
341     oc_mutex_unlock(context->lock);
342
343     c->recv_info.peer    = peer;
344     c->recv_info.context = context;
345
346     // The request characteristic only handles writes.
347     g_signal_connect(c->characteristic,
348                      "handle-write-value",
349                      G_CALLBACK(CAGattCharacteristicHandleWriteValue),
350                      c);
351
352     return true;
353 }
354
355 // ------------------------------------------------------------
356
357 bool CAGattResponseCharacteristicInitialize(struct CAGattService * s,
358                                             CALEContext * context)
359 {
360     CAGattCharacteristic * const c = &s->response_characteristic;
361
362     if (!CAGattCharacteristicInitialize(c,
363                                         context,
364                                         s,
365                                         CA_GATT_RESPONSE_CHRC_PATH,
366                                         CA_GATT_RESPONSE_CHRC_UUID,
367                                         "notify"))
368     {
369         return false;
370     }
371
372     c->service = s;
373
374     if (!CAGattResponseDescriptorInitialize(s, context->connection))
375     {
376         CAGattCharacteristicDestroy(c);
377         return false;
378     }
379
380     /*
381       The descriptor object path is not fixed at compile-time.
382       Retrieve the object path that was set at run-time.
383
384       Note that we don't explicitly add the Client Characteristic
385       Configuration Descriptor for the response characteristic since
386       that is done by BlueZ when the "notify" property is set.
387       Furthermore, a client requests notifications by calling the
388       org.bluez.GattCharacteristic1.StartNotify() method.
389       Consequently, there is no need for the client to explicitly
390       enable notifications by writing to the client characteristic
391       configuration descriptor.
392     */
393     char const * const descriptor_paths[] = {
394         c->descriptor.object_path,
395         NULL
396     };
397
398     gatt_characteristic1_set_descriptors(c->characteristic,
399                                          descriptor_paths);
400
401     // The response characteristic only handles notifications.
402     g_signal_connect(
403         c->characteristic,
404         "handle-start-notify",
405         G_CALLBACK(CAGattCharacteristicHandleStartNotify),
406         c);
407
408     g_signal_connect(
409         c->characteristic,
410         "handle-stop-notify",
411         G_CALLBACK(CAGattCharacteristicHandleStopNotify),
412         c);
413
414     return true;
415 }
416
417 void CAGattCharacteristicDestroy(CAGattCharacteristic * c)
418 {
419     assert(c != NULL);  // As designed, c is always non-NULL.
420
421     CAGattRecvInfoDestroy(&c->recv_info);
422
423     CAGattDescriptorDestroy(&c->descriptor);
424
425     g_clear_object(&c->characteristic);
426
427     g_free(c->object_path);
428     c->object_path = NULL;
429
430     c->service = NULL;
431     c->context = NULL;
432 }
433
434 // ---------------------------------------------------------------------
435 //                  GATT Characteristic Properties
436 // ---------------------------------------------------------------------
437
438
439 GVariant * CAGattCharacteristicGetProperties(
440     GattCharacteristic1 * characteristic)
441 {
442     /**
443      * Create a variant containing the @c GattCharacteristic1
444      * properties, of the form @c a{sa{sv}}.
445      *
446      * @note We don't care about the "Value" property here since it is
447      *       automatically made available by BlueZ on the client
448      *       side.
449      *
450      * @todo Should we care about "Notifying" property here?
451      */
452
453     /*
454       Populate the property table, and create the variant to be
455       embedded in the results of the
456       org.freedesktop.Dbus.ObjectManager.GetManagedObjects() method
457       call.
458     */
459     CADBusSkeletonProperty const properties[] = {
460         { "UUID",
461           g_variant_new_string(
462               gatt_characteristic1_get_uuid(characteristic)) },
463         { "Service",
464           g_variant_new_object_path(
465               gatt_characteristic1_get_service(characteristic)) },
466         { "Flags",
467           g_variant_new_strv(
468               gatt_characteristic1_get_flags(characteristic),
469               -1) },
470         { "Descriptors",
471           g_variant_new_objv(
472               gatt_characteristic1_get_descriptors(characteristic),
473               -1) }
474     };
475
476     return
477         CAMakePropertyDictionary(
478             BLUEZ_GATT_CHARACTERISTIC_INTERFACE,
479             properties,
480             sizeof(properties) / sizeof(properties[0]));
481 }