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"
27 #include "cagattservice.h"
28 #include "caremotehandler.h"
34 static char const TAG[] = "BLE_CHARACTERISTIC";
36 // ---------------------------------------------------------------------
37 // GATT Request Handling
38 // ---------------------------------------------------------------------
40 * Handle @c org.bluez.GattCharacterstic1.WriteValue() method call.
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.
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.
59 * @return @c TRUE to indicate that
60 * @c org.bluez.Characteristic.WriteValue() method is
63 static gboolean CAGattCharacteristicHandleWriteValue(
64 GattCharacteristic1 * object,
65 GDBusMethodInvocation * invocation,
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.
75 // GLib maps an octet to a guchar, which is of size 1.
77 gconstpointer const data =
78 g_variant_get_fixed_array(value, &len, 1);
80 CAGattCharacteristic * const c = user_data;
82 if (CAGattRecv(&c->recv_info, data, (uint32_t) len))
84 gatt_characteristic1_complete_write_value(object, invocation);
88 g_dbus_method_invocation_return_dbus_error(
90 "org.bluez.Error.Failed",
91 "Error when handling GATT request data fragment");
97 // ---------------------------------------------------------------------
98 // GATT Response Handling
99 // ---------------------------------------------------------------------
101 * Handle @c org.bluez.GattCharacterstic1.StartNotify() method call.
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.
109 * @note This handler is not available in the OIC GATT request
110 * characteristic implementation.
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.
119 * @return @c TRUE to indicate that
120 * @c org.bluez.Characteristic.StartNotify() method is
123 static gboolean CAGattCharacteristicHandleStartNotify(
124 GattCharacteristic1 * object,
125 GDBusMethodInvocation * invocation,
131 * Only allow the client to start notifications once.
133 * @todo Does BlueZ already prevent redundant calls to
134 * @c org.bluez.GattCharacteristic1.StartNotify()?
136 if (gatt_characteristic1_get_notifying(object))
138 g_dbus_method_invocation_return_dbus_error(
140 "org.bluez.Error.Failed",
141 "Notifications are already enabled.");
147 * @todo Do we need to explicitly emit the @c GObject @c notify or
148 * @c org.freedesktop.Dbus.Properties.PropertiesChanged
151 gatt_characteristic1_set_notifying(object, TRUE);
152 gatt_characteristic1_complete_start_notify(object, invocation);
158 * Handle @c org.bluez.GattCharacterstic1.StopNotify() method call.
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.
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.
173 * @return @c TRUE to indicate that
174 * @c org.bluez.Characteristic.StopNotify() method is
177 static gboolean CAGattCharacteristicHandleStopNotify(
178 GattCharacteristic1 * object,
179 GDBusMethodInvocation * invocation,
185 * @todo Does BlueZ already prevent redundant calls to
186 * @c org.bluez.GattCharacteristic1.StopNotify()?
188 if (!gatt_characteristic1_get_notifying(object))
190 g_dbus_method_invocation_return_dbus_error(
192 "org.bluez.Error.Failed",
193 "Notifications were not previously enabled.");
199 * @todo Do we need to explicitly emit the @c GObject @c notify or
200 * @c org.freedesktop.Dbus.Properties.PropertiesChanged
203 gatt_characteristic1_set_notifying(object, FALSE);
204 gatt_characteristic1_complete_stop_notify(object, invocation);
209 // ---------------------------------------------------------------------
210 // GATT Characteristic Lifecyle
211 // ---------------------------------------------------------------------
214 * Initialize GATT characteristic fields.
216 * This function initializes the @c CAGattCharacteristic object fields.
218 * @param[out] c GATT characteristic information to
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
225 * @param[in] s Information about GATT service
226 * to which the characteristic
228 * @param[in] characteristic_path @c GattCharacteristic1 object
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
237 * @note This function does not allocate the @a characteristic object
238 * itself. The caller is responsible for allocating that
241 * @todo Too many parameters. Perhaps pass in a pointer to a struct
244 static bool CAGattCharacteristicInitialize(
245 CAGattCharacteristic * c,
246 CALEContext * context,
248 char const * characteristic_path,
252 // Path of the form /org/iotivity/gatt/hci0/service0/char0.
254 g_strdup_printf("%s/%s", s->object_path, characteristic_path);
256 assert(g_variant_is_object_path(c->object_path));
258 c->context = context;
261 c->characteristic = gatt_characteristic1_skeleton_new();
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);
267 char const * const flags[] = { flag, NULL };
268 gatt_characteristic1_set_flags(c->characteristic, flags);
270 CAGattRecvInfoInitialize(&c->recv_info);
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),
280 CAGattCharacteristicDestroy(c);
284 "Unable to export D-Bus GATT characteristic "
296 // ------------------------------------------------------------
298 bool CAGattRequestCharacteristicInitialize(struct CAGattService * s,
299 CALEContext * context)
301 CAGattCharacteristic * const c = &s->request_characteristic;
303 if (!CAGattCharacteristicInitialize(c,
306 CA_GATT_REQUEST_CHRC_PATH,
307 CA_GATT_REQUEST_CHRC_UUID,
308 "write-without-response"))
313 if (!CAGattRequestDescriptorInitialize(s, context->connection))
315 CAGattCharacteristicDestroy(c);
320 The descriptor object path is not fixed at compile-time.
321 Retrieve the object path that was set at run-time.
323 char const * const descriptor_paths[] = {
324 c->descriptor.object_path,
328 gatt_characteristic1_set_descriptors(c->characteristic,
331 char * const peer = CAGattServiceMakePeerAddress(s);
335 CAGattCharacteristicDestroy(c);
339 ca_mutex_lock(context->lock);
340 c->recv_info.on_packet_received = context->on_server_received_data;
341 ca_mutex_unlock(context->lock);
343 c->recv_info.peer = peer;
344 c->recv_info.context = context;
346 // The request characteristic only handles writes.
347 g_signal_connect(c->characteristic,
348 "handle-write-value",
349 G_CALLBACK(CAGattCharacteristicHandleWriteValue),
355 // ------------------------------------------------------------
357 bool CAGattResponseCharacteristicInitialize(struct CAGattService * s,
358 CALEContext * context)
360 CAGattCharacteristic * const c = &s->response_characteristic;
362 if (!CAGattCharacteristicInitialize(c,
365 CA_GATT_RESPONSE_CHRC_PATH,
366 CA_GATT_RESPONSE_CHRC_UUID,
374 if (!CAGattResponseDescriptorInitialize(s, context->connection))
376 CAGattCharacteristicDestroy(c);
381 The descriptor object path is not fixed at compile-time.
382 Retrieve the object path that was set at run-time.
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.
393 char const * const descriptor_paths[] = {
394 c->descriptor.object_path,
398 gatt_characteristic1_set_descriptors(c->characteristic,
401 // The response characteristic only handles notifications.
404 "handle-start-notify",
405 G_CALLBACK(CAGattCharacteristicHandleStartNotify),
410 "handle-stop-notify",
411 G_CALLBACK(CAGattCharacteristicHandleStopNotify),
417 void CAGattCharacteristicDestroy(CAGattCharacteristic * c)
419 assert(c != NULL); // As designed, c is always non-NULL.
421 CAGattRecvInfoDestroy(&c->recv_info);
423 CAGattDescriptorDestroy(&c->descriptor);
425 g_clear_object(&c->characteristic);
427 g_free(c->object_path);
428 c->object_path = NULL;
434 // ---------------------------------------------------------------------
435 // GATT Characteristic Properties
436 // ---------------------------------------------------------------------
439 GVariant * CAGattCharacteristicGetProperties(
440 GattCharacteristic1 * characteristic)
443 * Create a variant containing the @c GattCharacteristic1
444 * properties, of the form @c a{sa{sv}}.
446 * @note We don't care about the "Value" property here since it is
447 * automatically made available by BlueZ on the client
450 * @todo Should we care about "Notifying" property here?
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
459 CADBusSkeletonProperty const properties[] = {
461 g_variant_new_string(
462 gatt_characteristic1_get_uuid(characteristic)) },
464 g_variant_new_object_path(
465 gatt_characteristic1_get_service(characteristic)) },
468 gatt_characteristic1_get_flags(characteristic),
472 gatt_characteristic1_get_descriptors(characteristic),
477 CAMakePropertyDictionary(
478 BLUEZ_GATT_CHARACTERISTIC_INTERFACE,
480 sizeof(properties) / sizeof(properties[0]));