1 /*****************************************************************
3 * Copyright 2015 Intel Corporation All Rights Reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 ******************************************************************/
19 #include "characteristic.h"
21 #include "gatt_dbus.h"
26 #include "oic_malloc.h"
27 #include "oic_string.h"
29 #include "cagattservice.h"
30 #include "caremotehandler.h"
38 static char const TAG[] = "BLE_CHARACTERISTIC";
40 // ---------------------------------------------------------------------
41 // GATT Request Handling
42 // ---------------------------------------------------------------------
44 * Handle @c org.bluez.GattCharacterstic1.WriteValue() method call.
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.
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.
63 * @return @c TRUE to indicate that
64 * @c org.bluez.Characteristic.WriteValue() method is
67 static gboolean CAGattCharacteristicHandleWriteValue(
68 GattCharacteristic1 * object,
69 GDBusMethodInvocation * invocation,
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.
80 gconstpointer const data =
81 g_variant_get_fixed_array(value,
83 1); // sizeof(guchar) == 1
85 CAGattCharacteristic * const c = user_data;
87 if (CAGattRecv(&c->recv_info, data, (uint32_t) len))
89 gatt_characteristic1_complete_write_value(object, invocation);
93 g_dbus_method_invocation_return_dbus_error(
95 "org.bluez.Error.Failed",
96 "Error when handling GATT request data fragment");
102 // ---------------------------------------------------------------------
103 // GATT Response Handling
104 // ---------------------------------------------------------------------
106 * Make the peer address corresponding to the given GATT
109 * @param[in] c Information about GATT characteristic for which the
110 * peer (client) @c CAEndpoint_t object is being
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.
117 static char * CAGattCharacteristicMakePeerAddress(
118 CAGattCharacteristic * c)
123 Length of stringified pointer in hexadecimal format, plus one
126 static size_t const PSEUDO_ADDR_LEN = sizeof(intptr_t) / 4 + 1;
128 assert(MAX_ADDR_STR_SIZE_CA > PSEUDO_ADDR_LEN);
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
136 1) only one LE central is ever connected to an LE peripheral
137 2) the CA layer doesn't directly interpret the address
139 char * const addr = OICMalloc(PSEUDO_ADDR_LEN);
140 int const count = snprintf(addr,
145 if (count >= (int) PSEUDO_ADDR_LEN)
149 "Error creating peer address on server side.");
158 * Handle @c org.bluez.GattCharacterstic1.StartNotify() method call.
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.
166 * @note This handler is not available in the OIC GATT request
167 * characteristic implementation.
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.
176 * @return @c TRUE to indicate that
177 * @c org.bluez.Characteristic.StartNotify() method is
180 static gboolean CAGattCharacteristicHandleStartNotify(
181 GattCharacteristic1 * object,
182 GDBusMethodInvocation * invocation,
186 * Only allow the client to start notifications once.
188 * @todo Does BlueZ already prevent redundant calls to
189 * @c org.bluez.GattCharacteristic1.StartNotify()?
191 if (gatt_characteristic1_get_notifying(object))
193 g_dbus_method_invocation_return_dbus_error(
195 "org.bluez.Error.Failed",
196 "Notifications are already enabled.");
201 // Retrieve the response characteristic information.
202 CAGattCharacteristic * const characteristic = user_data;
205 CAGattCharacteristicMakePeerAddress(characteristic);
209 g_dbus_method_invocation_return_dbus_error(
211 "org.bluez.Error.Failed",
212 "Error creating peer endpoint information");
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.
223 CALEContext * const context = characteristic->context;
225 ca_mutex_lock(context->lock);
227 #if GLIB_CHECK_VERSION(2,40,0)
229 GLib hash table functions started returning a boolean result in
232 bool const inserted =
234 g_hash_table_insert(context->characteristic_map,
238 ca_mutex_unlock(context->lock);
240 #if GLIB_CHECK_VERSION(2,40,0)
243 g_dbus_method_invocation_return_dbus_error(
245 "org.bluez.Error.Failed",
246 "Unable to set response endpoint.");
255 * @todo Do we need to explicitly emit the @c GObject @c notify or
256 * @c org.freedesktop.Dbus.Properties.PropertiesChanged
259 gatt_characteristic1_set_notifying(object, TRUE);
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.
267 CAGattRecvInfo * const recv_info =
268 &characteristic->service->request_characteristic.recv_info;
270 recv_info->peer = peer;
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);
277 gatt_characteristic1_complete_start_notify(object, invocation);
283 * Handle @c org.bluez.GattCharacterstic1.StopNotify() method call.
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.
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.
298 * @return @c TRUE to indicate that
299 * @c org.bluez.Characteristic.StopNotify() method is
302 static gboolean CAGattCharacteristicHandleStopNotify(
303 GattCharacteristic1 * object,
304 GDBusMethodInvocation * invocation,
307 assert(user_data != NULL);
310 * @todo Does BlueZ already prevent redundant calls to
311 * @c org.bluez.GattCharacteristic1.StopNotify()?
313 if (!gatt_characteristic1_get_notifying(object))
315 g_dbus_method_invocation_return_dbus_error(
317 "org.bluez.Error.Failed",
318 "Notifications were not previously enabled.");
323 CAGattCharacteristic * const characteristic = user_data;
325 // Clear the client endpoint from the request characteristic.
326 CAGattRecvInfo * const recv_info =
327 &characteristic->service->request_characteristic.recv_info;
330 Remove the appropriate entry from the endpoint-to-characteristic
331 map so that attempts to send a response through it will fail.
333 CALEContext * const context = characteristic->context;
334 ca_mutex_lock(context->lock);
337 g_hash_table_remove(context->characteristic_map, recv_info->peer);
339 ca_mutex_unlock(context->lock);
341 CAGattRecvInfoDestroy(recv_info);
344 * @todo Do we need to explicitly emit the @c GObject @c notify or
345 * @c org.freedesktop.Dbus.Properties.PropertiesChanged
348 gatt_characteristic1_set_notifying(object, FALSE);
352 gatt_characteristic1_complete_stop_notify(object, invocation);
356 g_dbus_method_invocation_return_dbus_error(
358 "org.bluez.Error.Failed",
359 "Error removing peer address information");
365 // ---------------------------------------------------------------------
366 // GATT Characteristic Lifecyle
367 // ---------------------------------------------------------------------
370 * Initialize GATT characteristic fields.
372 * This function initializes the @c CAGattCharacteristic object fields.
374 * @param[out] c GATT characteristic information to
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
381 * @param[in] s Information about GATT service
382 * to which the characteristic
384 * @param[in] characteristic_path @c GattCharacteristic1 object
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
393 * @note This function does not allocate the @a characteristic object
394 * itself. The caller is responsible for allocating that
397 * @todo Too many parameters. Perhaps pass in a pointer to a struct
400 static bool CAGattCharacteristicInitialize(
401 CAGattCharacteristic * c,
402 CALEContext * context,
404 char const * characteristic_path,
408 // Path of the form /org/iotivity/gatt/hci0/service0/char0.
410 g_strdup_printf("%s/%s", s->object_path, characteristic_path);
412 assert(g_variant_is_object_path(c->object_path));
414 c->context = context;
417 c->characteristic = gatt_characteristic1_skeleton_new();
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);
423 char const * flags[] = { flag, NULL };
424 gatt_characteristic1_set_flags(c->characteristic, flags);
426 CAGattRecvInfoInitialize(&c->recv_info);
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),
436 CAGattCharacteristicDestroy(c);
440 "Unable to export D-Bus GATT characteristic "
452 // ------------------------------------------------------------
454 bool CAGattRequestCharacteristicInitialize(struct CAGattService * s,
455 CALEContext * context)
457 CAGattCharacteristic * const c = &s->request_characteristic;
459 if (!CAGattCharacteristicInitialize(c,
462 CA_GATT_REQUEST_CHRC_PATH,
463 CA_GATT_REQUEST_CHRC_UUID,
464 "write-without-response"))
469 if (!CAGattRequestDescriptorInitialize(s, context->connection))
471 CAGattCharacteristicDestroy(c);
476 The descriptor object path is not fixed at compile-time.
477 Retrieve the object path that was set at run-time.
479 char const * descriptor_paths[] = {
480 c->descriptor.object_path,
484 gatt_characteristic1_set_descriptors(c->characteristic,
487 // The request characteristic only handles writes.
488 g_signal_connect(c->characteristic,
489 "handle-write-value",
490 G_CALLBACK(CAGattCharacteristicHandleWriteValue),
496 // ------------------------------------------------------------
498 bool CAGattResponseCharacteristicInitialize(struct CAGattService * s,
499 CALEContext * context)
501 CAGattCharacteristic * const c = &s->response_characteristic;
503 if (!CAGattCharacteristicInitialize(c,
506 CA_GATT_RESPONSE_CHRC_PATH,
507 CA_GATT_RESPONSE_CHRC_UUID,
515 if (!CAGattResponseDescriptorInitialize(s, context->connection))
517 CAGattCharacteristicDestroy(c);
522 The descriptor object path is not fixed at compile-time.
523 Retrieve the object path that was set at run-time.
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.
534 char const * descriptor_paths[] = {
535 c->descriptor.object_path,
539 gatt_characteristic1_set_descriptors(c->characteristic,
542 // The response characteristic only handles notifications.
545 "handle-start-notify",
546 G_CALLBACK(CAGattCharacteristicHandleStartNotify),
551 "handle-stop-notify",
552 G_CALLBACK(CAGattCharacteristicHandleStopNotify),
558 void CAGattCharacteristicDestroy(CAGattCharacteristic * c)
560 assert(c != NULL); // As designed, c is always non-NULL.
562 CAGattRecvInfoDestroy(&c->recv_info);
564 CAGattDescriptorDestroy(&c->descriptor);
566 g_clear_object(&c->characteristic);
568 g_free(c->object_path);
569 c->object_path = NULL;
575 // ---------------------------------------------------------------------
576 // GATT Characteristic Properties
577 // ---------------------------------------------------------------------
580 GVariant * CAGattCharacteristicGetProperties(
581 GattCharacteristic1 * characteristic)
584 * Create a variant containing the @c GattCharacteristic1
585 * properties, of the form @c a{sa{sv}}.
587 * @note We don't care about the "Value" property here since it is
588 * automatically made available by BlueZ on the client
591 * @todo Should we care about "Notifying" property here?
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
600 CADBusSkeletonProperty const properties[] = {
602 g_variant_new_string(
603 gatt_characteristic1_get_uuid(characteristic)) },
605 g_variant_new_object_path(
606 gatt_characteristic1_get_service(characteristic)) },
609 gatt_characteristic1_get_flags(characteristic),
613 gatt_characteristic1_get_descriptors(characteristic),
618 CAMakePropertyDictionary(
619 BLUEZ_GATT_CHARACTERISTIC_INTERFACE,
621 sizeof(properties) / sizeof(properties[0]));