[Adapt:HDP]Fix HDP app unexpected termination
[platform/core/connectivity/bluetooth-frwk.git] / bt-api / bt-hdp.c
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *              http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <gio/gio.h>
21 #include <glib.h>
22 #include <string.h>
23 #include <gio/gunixfdlist.h>
24
25 #include "bluetooth-api.h"
26 #include "bt-common.h"
27 #include "bt-internal-types.h"
28 #include "bt-request-sender.h"
29
30 #define HDP_BUFFER_SIZE 1024
31 #define BLUEZ_HDP_MANAGER_INTERFACE  "org.bluez.HealthManager1"
32 #define BLUEZ_HDP_DEVICE_INTERFACE  "org.bluez.HealthDevice1"
33 #define BLUEZ_HDP_CHANNEL_INTERFACE  "org.bluez.HealthChannel1"
34
35 typedef struct {
36         int fd;
37         unsigned int channel_id;
38         guint watch_id;
39         void *app_handle;
40 } hdp_obj_info_t;
41
42 typedef struct {
43         void *app_handle;
44         GSList *obj_info;
45 } hdp_app_list_t;
46
47 static GSList *g_app_list = NULL;
48
49 /* Variable for privilege, only for write API,
50  * before we should reduce time to bt-service dbus calling
51  * -1 : Don't have a permission to access API
52  * 0 : Initial value, not yet check
53  * 1 : Have a permission to access API
54  */
55 static int privilege_token;
56
57 static void __bt_hdp_obj_info_free(hdp_obj_info_t *info)
58 {
59         BT_DBG("+");
60
61         ret_if(NULL == info);
62
63         g_source_remove(info->watch_id);
64         close(info->fd);
65         g_free(info);
66
67         BT_DBG("-");
68 }
69
70 static hdp_app_list_t *__bt_hdp_internal_gslist_find_app_handler(void *app_handle)
71 {
72         GSList *l;
73
74         retv_if(g_app_list == NULL, NULL);
75
76         BT_DBG("List length = %d\n", g_slist_length(g_app_list));
77
78         for (l = g_app_list; l != NULL; l = l->next) {
79                 hdp_app_list_t *list = l->data;
80
81                 if (list) {
82                         if (0 == g_strcmp0((char *)list->app_handle,
83                                                 (char *)app_handle))
84                                 return list;
85                 }
86         }
87
88         return NULL;
89 }
90
91 static hdp_obj_info_t *__bt_hdp_internal_gslist_obj_find_using_ch_id(unsigned int channel_id)
92 {
93         GSList *l;
94         GSList *iter;
95
96         retv_if(g_app_list == NULL, NULL);
97
98         BT_DBG("List length = %d\n", g_slist_length(g_app_list));
99
100         for (l = g_app_list; l != NULL; l = l->next) {
101                 hdp_app_list_t *list = l->data;
102                 if (!list)
103                         return NULL;
104
105                 for (iter = list->obj_info; iter != NULL; iter = iter->next) {
106                         hdp_obj_info_t *info = iter->data;
107                         if (!info)
108                                 return NULL;
109
110                         if (channel_id == info->channel_id)
111                                 return info;
112                 }
113         }
114
115         return NULL;
116 }
117
118 static hdp_obj_info_t *__bt_hdp_internal_gslist_obj_find_using_fd(unsigned int sock_fd)
119 {
120         GSList *l;
121         GSList *iter;
122
123         retv_if(g_app_list == NULL, NULL);
124
125         BT_DBG("List length = %d\n", g_slist_length(g_app_list));
126
127         for (l = g_app_list; l != NULL; l = l->next) {
128                 hdp_app_list_t *list = l->data;
129                 if (!list)
130                         return NULL;
131
132                 for (iter = list->obj_info; iter != NULL; iter = iter->next) {
133                         hdp_obj_info_t *info = iter->data;
134                         if (!info)
135                                 return NULL;
136
137                         if (sock_fd == info->fd)
138                                 return info;
139                 }
140         }
141
142         return NULL;
143 }
144
145 static gboolean __bt_hdp_internal_data_received(GIOChannel *gio,
146                                         GIOCondition cond, gpointer data)
147 {
148         int fd;
149         gsize len = 0;
150         bt_user_info_t *user_info;
151         char buff[HDP_BUFFER_SIZE] = { 0, };
152         GError *err = NULL;
153         GIOStatus status = G_IO_STATUS_NORMAL;
154         bluetooth_device_address_t *address = data;
155         hdp_obj_info_t *info;
156
157         BT_DBG("+");
158
159         fd = g_io_channel_unix_get_fd(gio);
160         BT_DBG("fd: %d", fd);
161
162         info = __bt_hdp_internal_gslist_obj_find_using_fd(fd);
163         if (!info) {
164                 BT_INFO("No obj info found for fd: %d", fd);
165                 g_free(address);
166                 return FALSE;
167         }
168
169         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
170                 bt_hdp_disconnected_t disconn_info;
171                 bt_user_info_t *user_info;
172                 hdp_app_list_t *list;
173
174                 BT_DBG("GIOCondition %d", cond);
175                 disconn_info.channel_id = info->channel_id;
176                 memcpy(&disconn_info.device_address, address, sizeof(bluetooth_device_address_t));
177
178                 list = __bt_hdp_internal_gslist_find_app_handler(info->app_handle);
179                 if (list) {
180                         list->obj_info = g_slist_remove(list->obj_info, info);
181                         __bt_hdp_obj_info_free(info);
182                 }
183
184                 user_info = _bt_get_user_data(BT_COMMON);
185                 _bt_common_event_cb(BLUETOOTH_EVENT_HDP_DISCONNECTED,
186                                 BLUETOOTH_ERROR_NONE, &disconn_info,
187                                 user_info->cb, user_info->user_data);
188                 g_free(address);
189                 return FALSE;
190         }
191
192         status = g_io_channel_read_chars(gio, buff, HDP_BUFFER_SIZE, &len, &err);
193         if (status != G_IO_STATUS_NORMAL) {
194                 BT_ERR("IO Channel read is failed with %d", status);
195                 if (err) {
196                         BT_ERR("IO Channel read error [%s]", err->message);
197                         g_error_free(err);
198                 }
199                 g_free(address);
200                 return FALSE;
201         }
202
203         BT_DBG("fd: %d, len: %d, buffer: %s", fd, len, buff);
204
205         user_info = _bt_get_user_data(BT_COMMON);
206         if (user_info->cb) {
207                 bt_hdp_data_ind_t data_ind = { 0, };
208
209                 data_ind.channel_id = info->channel_id;
210                 data_ind.buffer = buff;
211                 data_ind.size = len;
212                 _bt_common_event_cb(BLUETOOTH_EVENT_HDP_DATA_RECEIVED,
213                                 BLUETOOTH_ERROR_NONE, &data_ind,
214                                 user_info->cb, user_info->user_data);
215         }
216
217         BT_DBG("-");
218         return TRUE;
219 }
220
221 static void __hdp_handle_new_connection(bt_hdp_connected_t *conn_info, int fd)
222 {
223         hdp_obj_info_t *info;
224         hdp_app_list_t *list;
225         bluetooth_device_address_t *address;
226         GIOChannel *gio;
227
228         BT_DBG("+");
229
230         list = __bt_hdp_internal_gslist_find_app_handler((void *)conn_info->app_handle);
231         if (NULL == list) {
232                 BT_ERR("**** Could not locate the list for %s*****\n", conn_info->app_handle);
233                 return;
234         }
235
236         info = g_new0(hdp_obj_info_t, 1);
237         info->channel_id = conn_info->channel_id;
238         info->app_handle = list->app_handle;
239         info->fd = fd;
240         address = g_memdup(&(conn_info->device_address), sizeof(bluetooth_device_address_t));
241
242         gio = g_io_channel_unix_new(fd);
243         g_io_channel_set_close_on_unref(gio, TRUE);
244         info->watch_id = g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
245                         __bt_hdp_internal_data_received, (void *)address);
246         g_io_channel_unref(gio);
247         list->obj_info = g_slist_append(list->obj_info, info);
248
249         BT_DBG("-");
250 }
251
252 void _bt_hdp_app_remove_obj_info(unsigned int channel_id)
253 {
254         hdp_app_list_t *list;
255         hdp_obj_info_t *info;
256
257         BT_DBG("+");
258
259         info = __bt_hdp_internal_gslist_obj_find_using_ch_id(channel_id);
260         ret_if(NULL == info);
261
262         list = __bt_hdp_internal_gslist_find_app_handler(info->app_handle);
263         ret_if(NULL == list);
264
265         list->obj_info = g_slist_remove(list->obj_info, info);
266         BT_DBG("obj_info length = %d\n", g_slist_length(list->obj_info));
267         __bt_hdp_obj_info_free(info);
268
269         BT_DBG("-");
270 }
271
272 int _bt_hdp_app_acquire_fd(bt_hdp_connected_t *conn_info)
273 {
274         int result;
275         GUnixFDList *out_fd_list = NULL;
276
277         BT_INIT_PARAMS();
278         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
279
280         g_array_append_vals(in_param1, &conn_info->channel_id, sizeof(int));
281         result = _bt_send_request_with_unix_fd_list(BT_BLUEZ_SERVICE, BT_HDP_GET_FD,
282                         in_param1, in_param2, in_param3, in_param4, NULL, &out_param, &out_fd_list);
283
284         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
285
286         BT_DBG("result: %x", result);
287         if (result != BLUETOOTH_ERROR_NONE || NULL == out_fd_list) {
288                 BT_ERR("out_fd_list is NULL");
289                 bluetooth_hdp_disconnect(conn_info->channel_id, &conn_info->device_address);
290                 return BLUETOOTH_ERROR_INTERNAL;
291         } else {
292                 int *fd_list_array;
293                 int len = 0;
294
295                 fd_list_array = g_unix_fd_list_steal_fds(out_fd_list, &len);
296                 BT_INFO("Num fds in fd_list is : %d, fd_list[0]: %d", len, fd_list_array[0]);
297                 __hdp_handle_new_connection(conn_info, fd_list_array[0]);
298                 g_free(fd_list_array);
299                 g_object_unref(out_fd_list);
300         }
301
302         BT_DBG("-");
303         return BLUETOOTH_ERROR_NONE;
304 }
305
306 /**********************************************************************
307 *                       Health device APIs (HDP)                        *
308 ***********************************************************************/
309 BT_EXPORT_API int bluetooth_hdp_activate(unsigned short data_type,
310                                         bt_hdp_role_type_t role,
311                                         bt_hdp_qos_type_t channel_type,
312                                         char **app_handle)
313 {
314         int result = BLUETOOTH_ERROR_NONE;
315
316         BT_DBG("+");
317
318         BT_CHECK_ENABLED(return);
319
320         /*For source role is mandatory */
321         if (role == HDP_ROLE_SOURCE && channel_type == HDP_QOS_ANY) {
322                 BT_ERR("For source, type is mandatory - Reliable/Streaming");
323                 return BLUETOOTH_ERROR_INVALID_PARAM;
324         }
325
326         BT_INIT_PARAMS();
327         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
328
329         g_array_append_vals(in_param1, &data_type, sizeof(short));
330         g_array_append_vals(in_param2, &role, sizeof(bt_hdp_role_type_t));
331         g_array_append_vals(in_param3, &channel_type, sizeof(bt_hdp_qos_type_t));
332
333         result = _bt_send_request(BT_BLUEZ_SERVICE, BT_HDP_CREATE_APPLICATION,
334                 in_param1, in_param2, in_param3, in_param4, &out_param);
335
336         if (result == BLUETOOTH_ERROR_NONE) {
337                 char *buf;
338                 hdp_app_list_t *list;
339
340                 buf = &g_array_index(out_param, char, 0);
341                 BT_DBG("Created app: %s", buf);
342
343                 list = g_new0(hdp_app_list_t, 1);
344                 list->app_handle = (void *)g_strdup(buf);
345                 *app_handle = list->app_handle;
346                 g_app_list = g_slist_append(g_app_list, list);
347         }
348
349         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
350
351         BT_DBG("-");
352         return result;
353 }
354
355 BT_EXPORT_API int bluetooth_hdp_deactivate(const char *app_handle)
356 {
357         int result;
358         hdp_app_list_t *list;
359
360         BT_DBG("+");
361
362         BT_CHECK_ENABLED(return);
363         BT_CHECK_PARAMETER(app_handle, return);
364
365         list = __bt_hdp_internal_gslist_find_app_handler((void *)app_handle);
366         if (NULL == list) {
367                 BT_ERR("**** list not found for %s ******\n", app_handle);
368                 return BLUETOOTH_ERROR_INVALID_PARAM;
369         }
370
371         BT_DBG("app_handle: %s", app_handle);
372
373         BT_INIT_PARAMS();
374         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
375
376         g_array_append_vals(in_param1, app_handle, (strlen(app_handle) + 1));
377
378         result = _bt_send_request(BT_BLUEZ_SERVICE, BT_HDP_DESTROY_APPLICATION,
379                 in_param1, in_param2, in_param3, in_param4, &out_param);
380         if (result == BLUETOOTH_ERROR_NONE) {
381                 g_app_list = g_slist_remove(g_app_list, list);
382                 g_free(list->app_handle);
383                 g_slist_foreach(list->obj_info, (GFunc)__bt_hdp_obj_info_free, NULL);
384                 g_free(list);
385         }
386
387         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
388
389         return result;
390 }
391
392 BT_EXPORT_API int bluetooth_hdp_send_data(unsigned int channel_id,
393                                             const char *buffer,
394                                             unsigned int size)
395 {
396         int wbytes = 0;
397         int written = 0;
398         hdp_obj_info_t *info;
399         int result;
400
401         BT_DBG("+");
402
403         BT_CHECK_ENABLED(return);
404
405         retv_if (NULL == buffer, BLUETOOTH_ERROR_INVALID_PARAM);
406         retv_if (0 == size, BLUETOOTH_ERROR_INVALID_PARAM);
407
408         info = __bt_hdp_internal_gslist_obj_find_using_ch_id(channel_id);
409         if (NULL == info) {
410                 BT_ERR("*** Could not locate the info for %d*****", channel_id);
411                 return BLUETOOTH_ERROR_INVALID_PARAM;
412         }
413
414         switch (privilege_token) {
415         case 0:
416                 result = _bt_check_privilege(BT_BLUEZ_SERVICE, BT_HDP_SEND_DATA);
417
418                 if (result == BLUETOOTH_ERROR_NONE) {
419                         privilege_token = 1; /* Have a permission */
420                 } else if (result == BLUETOOTH_ERROR_PERMISSION_DEINED) {
421                         BT_ERR("Don't have a privilege to use this API");
422                         privilege_token = -1; /* Don't have a permission */
423                         return BLUETOOTH_ERROR_PERMISSION_DEINED;
424                 } else {
425                         /* Just break - It is not related with permission error */
426                 }
427                 break;
428         case 1:
429                 /* Already have a privilege */
430                 break;
431         case -1:
432                 return BLUETOOTH_ERROR_PERMISSION_DEINED;
433         default:
434                 /* Invalid privilge token value */
435                 return BLUETOOTH_ERROR_INTERNAL;
436         }
437
438         while (wbytes < size) {
439                 written = write(info->fd, (buffer + wbytes), (size - wbytes));
440                 if (written <= 0) {
441                         BT_ERR("write failed..\n");
442                         return BLUETOOTH_ERROR_NOT_IN_OPERATION;
443                 }
444                 wbytes += written;
445         }
446
447         return BLUETOOTH_ERROR_NONE;
448 }
449
450 BT_EXPORT_API int bluetooth_hdp_connect(const char *app_handle,
451                         bt_hdp_qos_type_t channel_type,
452                         const bluetooth_device_address_t *device_address)
453 {
454         int result;
455         hdp_app_list_t *list;
456         bt_user_info_t *user_info;
457
458         BT_CHECK_ENABLED(return);
459         BT_CHECK_PARAMETER(app_handle, return);
460         BT_CHECK_PARAMETER(device_address, return);
461
462         user_info = _bt_get_user_data(BT_COMMON);
463         retv_if(user_info->cb == NULL, BLUETOOTH_ERROR_INTERNAL);
464
465         list = __bt_hdp_internal_gslist_find_app_handler((void *)app_handle);
466         if (NULL == list) {
467                 BT_ERR("**** list not found for %s ******\n", app_handle);
468                 return BLUETOOTH_ERROR_INVALID_PARAM;
469         }
470
471         BT_DBG("app_handle: %s", app_handle);
472
473         BT_INIT_PARAMS();
474         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
475
476         g_array_append_vals(in_param1, app_handle, (strlen(app_handle) + 1));
477         g_array_append_vals(in_param2, &channel_type, sizeof(bt_hdp_qos_type_t));
478         g_array_append_vals(in_param3, device_address, sizeof(bluetooth_device_address_t));
479
480         result = _bt_send_request_async(BT_BLUEZ_SERVICE,
481                         BT_HDP_CONNECT, in_param1, in_param2, in_param3, in_param4,
482                         user_info->cb, user_info->user_data);
483
484         if (result != BLUETOOTH_ERROR_NONE)
485                 BT_ERR("BT_HDP_CONNECT failed");
486
487         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
488
489         return result;
490 }
491
492 BT_EXPORT_API int bluetooth_hdp_disconnect(unsigned int channel_id,
493                         const bluetooth_device_address_t *device_address)
494 {
495         int result;
496         hdp_obj_info_t *info;
497         bt_user_info_t *user_info;
498
499         BT_CHECK_ENABLED(return);
500         BT_CHECK_PARAMETER(device_address, return);
501
502         user_info = _bt_get_user_data(BT_COMMON);
503         retv_if(user_info->cb == NULL, BLUETOOTH_ERROR_INTERNAL);
504
505         BT_DBG("channel_id: %d", channel_id);
506         info = __bt_hdp_internal_gslist_obj_find_using_ch_id(channel_id);
507         if (NULL == info) {
508                 BT_ERR("*** Could not locate the info for %d*****", channel_id);
509                 return BLUETOOTH_ERROR_INVALID_PARAM;
510         }
511
512         BT_INIT_PARAMS();
513         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
514
515         g_array_append_vals(in_param1, &channel_id, sizeof(int));
516         g_array_append_vals(in_param2, device_address, sizeof(bluetooth_device_address_t));
517
518         result = _bt_send_request_async(BT_BLUEZ_SERVICE, BT_HDP_DISCONNECT,
519                         in_param1, in_param2, in_param3, in_param4,
520                         user_info->cb, user_info->user_data);
521         if (result != BLUETOOTH_ERROR_NONE)
522                 BT_ERR("BT_HDP_DISCONNECT failed");
523
524         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
525         return result;
526 }