Imported Upstream version 1.1.0
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / bt_le_adapter / linux / service.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 "service.h"
20 #include "gatt_dbus.h"
21 #include "utils.h"
22 #include "bluez.h"
23
24 #include "cagattservice.h"
25 #include "logger.h"
26 #include "oic_malloc.h"
27
28 #include <inttypes.h>
29 #include <stdio.h>
30 #include <assert.h>
31
32
33 // Logging tag.
34 static char const TAG[] = "BLE_SERVICE";
35
36 static GVariant * CAGattServiceGetProperties(GattService1 * service)
37 {
38     /*
39       Create a variant containing the @c GattService1 properties, of
40       the form @c a{sa{sv}}.
41     */
42
43     /**
44      * Populate the property table, and create the variant to be
45      * embedded in the results of the
46      * @c org.freedesktop.Dbus.ObjectManager.GetManagedObjects()
47      * method call.
48      *
49      * The @c "Device" property is only available on the client side
50      * so we don't bother returning it here.
51      *
52      * @todo Do we care about the @c "Includes" property?
53      *       @c "Includes" isn't implemented by BlueZ as of version
54      *       5.30, so we can leave it out.
55     */
56     CADBusSkeletonProperty const properties[] = {
57         { "UUID",
58           g_variant_new_string(gatt_service1_get_uuid(service)) },
59         { "Primary",
60           g_variant_new_boolean(gatt_service1_get_primary(service)) },
61         { "Characteristics",
62           g_variant_new_objv(
63               gatt_service1_get_characteristics(service),
64               -1) }
65     };
66
67     return
68         CAMakePropertyDictionary(
69             BLUEZ_GATT_SERVICE_INTERFACE,
70             properties,
71             sizeof(properties) / sizeof(properties[0]));
72 }
73
74 /**
75  * Implementation of the
76  * @c org.freedesktop.DBus.ObjectManager.GetManagedObjects() method
77  * for the @c org.bluez.GattService1 interface.
78  */
79 static gboolean CAGattServiceHandleGetManagedObjects(
80     ObjectManager * object,
81     GDBusMethodInvocation * invocation,
82     gpointer user_data)
83 {
84     /**
85      * @note Ideally we shouldn't need this implementation, and should
86      *       be able to simply rely GDBusObjectManagerServer instead.
87      *       Unfortunately, BlueZ expects the @c
88      *       org.bluez.GattService1 object to implement the @c
89      *       ObjectManager interface, and both interfaces must rooted
90      *       at the same object path, as well.  That requirement
91      *       prevents us from using @c GDBusObjectManagerServer since
92      *       it won't allow us to export more than interface on a
93      *       given object path.
94      */
95
96     /*
97       Build the object array containing the IoTivity
98       org.bluez.GattService1 hierarchy.
99
100       a{oa{sa{sv}}}
101     */
102     CAGattService * const service = user_data;
103
104     GVariantBuilder builder;
105     g_variant_builder_init(&builder, G_VARIANT_TYPE("a{oa{sa{sv}}}"));
106
107     // Start out with the service itself.
108     g_variant_builder_add(&builder,
109                           "{o@a{sa{sv}}}",
110                           service->object_path,
111                           CAGattServiceGetProperties(service->service));
112
113     /*
114       Add the request characteristic and user description
115       descriptor.
116     */
117     CAGattCharacteristic * const request_chrc =
118         &service->request_characteristic;
119
120     g_variant_builder_add(&builder,
121                           "{o@a{sa{sv}}}",
122                           request_chrc->object_path,
123                           CAGattCharacteristicGetProperties(
124                               request_chrc->characteristic));
125
126     CAGattDescriptor * const request_desc = &request_chrc->descriptor;
127
128     g_variant_builder_add(&builder,
129                           "{o@a{sa{sv}}}",
130                           request_desc->object_path,
131                           CAGattDescriptorGetProperties(
132                               request_desc->descriptor));
133
134     /*
135       Add the response characteristic and user description
136       descriptor.
137     */
138     CAGattCharacteristic * const response_chrc =
139         &service->response_characteristic;
140
141     g_variant_builder_add(&builder,
142                           "{o@a{sa{sv}}}",
143                           response_chrc->object_path,
144                           CAGattCharacteristicGetProperties(
145                               response_chrc->characteristic));
146
147     CAGattDescriptor * const response_desc = &response_chrc->descriptor;
148
149     g_variant_builder_add(&builder,
150                           "{o@a{sa{sv}}}",
151                           response_desc->object_path,
152                           CAGattDescriptorGetProperties(
153                               response_desc->descriptor));
154
155     GVariant * const objects = g_variant_builder_end(&builder);
156
157     object_manager_complete_get_managed_objects(object,
158                                                 invocation,
159                                                 objects);
160
161     return TRUE;
162 }
163
164 char * CAGattServiceMakePeerAddress(CAGattService * s)
165 {
166     assert(s != NULL);
167
168     /*
169       Since there is no direct way to obtain the client address
170       associated with the GATT characterstics on the server side,
171       embed a stringified pointer to this GATT service of the form
172       "&ABCDEF01" as the address instead so that we can use it to
173       refer to the client.  This works since:
174           1) only one LE central is ever connected to an LE peripheral
175           2) the CA layer doesn't directly interpret the client
176              address
177      */
178
179     /*
180       Length of stringified pointer in hexadecimal format, plus one
181       for the leading ampersand, and one more for the null
182       terminator.
183     */
184     static size_t const PSEUDO_ADDR_LEN = sizeof(uintptr_t) * 2 + 2;
185
186     assert(MAX_ADDR_STR_SIZE_CA > PSEUDO_ADDR_LEN);
187
188     char * const addr = OICMalloc(PSEUDO_ADDR_LEN);
189
190     if (addr == NULL)
191     {
192         return addr;
193     }
194
195     int const count = snprintf(addr,
196                                PSEUDO_ADDR_LEN,
197                                "&%" PRIxPTR,
198                                (uintptr_t) s);
199
200     if (count < 0 || count >= (int) PSEUDO_ADDR_LEN)
201     {
202         OIC_LOG(ERROR,
203                 TAG,
204                 "Error creating peer address on server side.");
205
206         OICFree(addr);
207
208         return NULL;
209     }
210
211     return addr;
212 }
213
214 CAGattService * CAGattServiceDecodeAddress(char const * address)
215 {
216     /*
217       The peer address is actually the value of the pointer to the
218       CAGattService object containing the response characteristic.
219      */
220     uintptr_t s = (uintptr_t) NULL;
221     (void) sscanf(address, "&%" SCNxPTR, &s);
222
223     return (CAGattService *) s;
224 }
225
226 bool CAGattServiceInitialize(CAGattService * s,
227                              char const * hci_name,
228                              CALEContext * context)
229 {
230     assert(s != NULL);
231     assert(context != NULL);
232     assert(hci_name != NULL);
233
234     // Path of the form /org/iotivity/gatt/hci0/service0.
235     s->object_path =
236         g_strdup_printf("%s/%s/%s",
237                         CA_GATT_SERVICE_ROOT_PATH,
238                         hci_name,
239                         CA_GATT_SERVICE_PATH);
240
241     assert(g_variant_is_object_path(s->object_path));
242
243     s->object_manager = object_manager_skeleton_new();
244     s->service = gatt_service1_skeleton_new();
245
246     gatt_service1_set_uuid(s->service, CA_GATT_SERVICE_UUID);
247     gatt_service1_set_primary(s->service, TRUE);
248
249     if (!CAGattRequestCharacteristicInitialize(s, context)
250         || !CAGattResponseCharacteristicInitialize(s, context))
251     {
252         CAGattServiceDestroy(s);
253         return false;
254     }
255
256     /*
257       The characteristic object paths are not fixed at compile-time.
258       Retrieve the object paths that were set at run-time.
259     */
260     char const * const characteristic_paths[] = {
261         s->request_characteristic.object_path,
262         s->response_characteristic.object_path,
263         NULL
264     };
265
266     gatt_service1_set_characteristics(s->service, characteristic_paths);
267
268     /*
269       Set the org.freedesktop.DBus.ObjectManager.GetManagedObjects()
270       handler for our BlueZ GATT service.
271     */
272     g_signal_connect(
273         s->object_manager,
274         "handle-get-managed-objects",
275         G_CALLBACK(CAGattServiceHandleGetManagedObjects),
276         s);
277
278     /*
279       BlueZ expects both the org.freedesktop.DBus.ObjectManager and
280       org.bluez.GattService1 interfaces to be rooted at the same
281       object path.  Export the service and object manager interface
282       skeletons with the same object path.
283      */
284     GError * error = NULL;
285     if (!g_dbus_interface_skeleton_export(
286             G_DBUS_INTERFACE_SKELETON(s->object_manager),
287             context->connection,
288             s->object_path,
289             &error)
290         || !g_dbus_interface_skeleton_export(
291             G_DBUS_INTERFACE_SKELETON(s->service),
292             context->connection,
293             s->object_path,
294             &error))
295     {
296         OIC_LOG_V(ERROR,
297                   TAG,
298                   "Unable to export GATT service interfaces: %s",
299                   error->message);
300
301         return false;
302     }
303
304     return true;
305 }
306
307 void CAGattServiceDestroy(CAGattService * s)
308 {
309     /**
310      * @todo If necessary, emit the
311      *       @c org.freedesktop.DBus.ObjectManager.InterfacesRemoved
312      *       signal via @c object_manager_emit_interfaces_removed() if
313      *       the CA GATT service objects were removed from the
314      *       @c ObjectManager.
315      */
316
317     assert(s != NULL);  // As designed, s is always non-NULL.
318
319     g_clear_object(&s->gatt_manager);
320
321     CAGattCharacteristicDestroy(&s->response_characteristic);
322     CAGattCharacteristicDestroy(&s->request_characteristic);
323
324     g_clear_object(&s->service);
325     g_clear_object(&s->object_manager);
326
327     g_free(s->object_path);
328     s->object_path = NULL;
329 }