f8f0fadca12612b78bad5be69b462d02eba54bc6
[platform/core/connectivity/bluetooth-frwk.git] / 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 ((int)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                 hdp_app_list_t *list;
172
173                 BT_DBG("GIOCondition %d", cond);
174                 disconn_info.channel_id = info->channel_id;
175                 memcpy(&disconn_info.device_address, address, sizeof(bluetooth_device_address_t));
176
177                 list = __bt_hdp_internal_gslist_find_app_handler(info->app_handle);
178                 if (list) {
179                         list->obj_info = g_slist_remove(list->obj_info, info);
180                         __bt_hdp_obj_info_free(info);
181                 }
182
183                 user_info = _bt_get_user_data(BT_COMMON);
184                 _bt_common_event_cb(BLUETOOTH_EVENT_HDP_DISCONNECTED,
185                                 BLUETOOTH_ERROR_NONE, &disconn_info,
186                                 user_info->cb, user_info->user_data);
187                 g_free(address);
188                 return FALSE;
189         }
190
191         status = g_io_channel_read_chars(gio, buff, HDP_BUFFER_SIZE, &len, &err);
192         if (status != G_IO_STATUS_NORMAL) {
193                 BT_ERR("IO Channel read is failed with %d", status);
194                 if (err) {
195                         BT_ERR("IO Channel read error [%s]", err->message);
196                         g_error_free(err);
197                 }
198                 g_free(address);
199                 return FALSE;
200         }
201
202         BT_DBG("fd: %d, len: %zd, buffer: %s", fd, len, buff);
203
204         user_info = _bt_get_user_data(BT_COMMON);
205         if (user_info->cb) {
206                 bt_hdp_data_ind_t data_ind = { 0, };
207
208                 data_ind.channel_id = info->channel_id;
209                 data_ind.buffer = buff;
210                 data_ind.size = len;
211                 _bt_common_event_cb(BLUETOOTH_EVENT_HDP_DATA_RECEIVED,
212                                 BLUETOOTH_ERROR_NONE, &data_ind,
213                                 user_info->cb, user_info->user_data);
214         }
215
216         BT_DBG("-");
217         return TRUE;
218 }
219
220 static void __hdp_handle_new_connection(bt_hdp_connected_t *conn_info, int fd)
221 {
222         hdp_obj_info_t *info;
223         hdp_app_list_t *list;
224         bluetooth_device_address_t *address;
225         GIOChannel *gio;
226
227         BT_DBG("+");
228
229         list = __bt_hdp_internal_gslist_find_app_handler((void *)conn_info->app_handle);
230         if (NULL == list) {
231                 BT_ERR("**** Could not locate the list for %s*****\n", conn_info->app_handle);
232                 return;
233         }
234
235         info = g_new0(hdp_obj_info_t, 1);
236         info->channel_id = conn_info->channel_id;
237         info->app_handle = list->app_handle;
238         info->fd = fd;
239         address = g_memdup(&(conn_info->device_address), sizeof(bluetooth_device_address_t));
240
241         gio = g_io_channel_unix_new(fd);
242         g_io_channel_set_close_on_unref(gio, TRUE);
243         info->watch_id = g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
244                         __bt_hdp_internal_data_received, (void *)address);
245         g_io_channel_unref(gio);
246         list->obj_info = g_slist_append(list->obj_info, info);
247
248         BT_DBG("-");
249 }
250
251 void _bt_hdp_app_remove_obj_info(unsigned int channel_id)
252 {
253         hdp_app_list_t *list;
254         hdp_obj_info_t *info;
255
256         BT_DBG("+");
257
258         info = __bt_hdp_internal_gslist_obj_find_using_ch_id(channel_id);
259         ret_if(NULL == info);
260
261         list = __bt_hdp_internal_gslist_find_app_handler(info->app_handle);
262         ret_if(NULL == list);
263
264         list->obj_info = g_slist_remove(list->obj_info, info);
265         BT_DBG("obj_info length = %d\n", g_slist_length(list->obj_info));
266         __bt_hdp_obj_info_free(info);
267
268         BT_DBG("-");
269 }
270
271 int _bt_hdp_app_acquire_fd(bt_hdp_connected_t *conn_info)
272 {
273         int result;
274         GUnixFDList *out_fd_list = NULL;
275
276         BT_INIT_PARAMS();
277         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
278
279         g_array_append_vals(in_param1, &conn_info->channel_id, sizeof(int));
280         result = _bt_send_request_with_unix_fd_list(BT_BLUEZ_SERVICE, BT_HDP_GET_FD,
281                         in_param1, in_param2, in_param3, in_param4, NULL, &out_param, &out_fd_list);
282
283         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
284
285         BT_DBG("result: %x", result);
286         if (result != BLUETOOTH_ERROR_NONE || NULL == out_fd_list) {
287                 BT_ERR("out_fd_list is NULL");
288                 bluetooth_hdp_disconnect(conn_info->channel_id, &conn_info->device_address);
289                 return BLUETOOTH_ERROR_INTERNAL;
290         } else {
291                 int *fd_list_array;
292                 int len = 0;
293
294                 fd_list_array = g_unix_fd_list_steal_fds(out_fd_list, &len);
295                 BT_INFO("Num fds in fd_list is : %d, fd_list[0]: %d", len, fd_list_array[0]);
296                 __hdp_handle_new_connection(conn_info, fd_list_array[0]);
297                 g_free(fd_list_array);
298                 g_object_unref(out_fd_list);
299         }
300
301         BT_DBG("-");
302         return BLUETOOTH_ERROR_NONE;
303 }
304
305 /**********************************************************************
306 *                       Health device APIs (HDP)                        *
307 ***********************************************************************/
308 BT_EXPORT_API int bluetooth_hdp_activate(unsigned short data_type,
309                                         bt_hdp_role_type_t role,
310                                         bt_hdp_qos_type_t channel_type,
311                                         char **app_handle)
312 {
313         int result = BLUETOOTH_ERROR_NONE;
314
315         BT_DBG("+");
316
317         BT_CHECK_ENABLED(return);
318
319         /*For source role is mandatory */
320         if (role == HDP_ROLE_SOURCE && channel_type == HDP_QOS_ANY) {
321                 BT_ERR("For source, type is mandatory - Reliable/Streaming");
322                 return BLUETOOTH_ERROR_INVALID_PARAM;
323         }
324
325         BT_INIT_PARAMS();
326         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
327
328         g_array_append_vals(in_param1, &data_type, sizeof(short));
329         g_array_append_vals(in_param2, &role, sizeof(bt_hdp_role_type_t));
330         g_array_append_vals(in_param3, &channel_type, sizeof(bt_hdp_qos_type_t));
331
332         result = _bt_send_request(BT_BLUEZ_SERVICE, BT_HDP_REGISTER_SINK_APP,
333                 in_param1, in_param2, in_param3, in_param4, &out_param);
334
335         if (result == BLUETOOTH_ERROR_NONE) {
336                 char *buf;
337                 hdp_app_list_t *list;
338
339                 buf = &g_array_index(out_param, char, 0);
340                 BT_DBG("Created app: %s", buf);
341
342                 list = g_new0(hdp_app_list_t, 1);
343                 list->app_handle = (void *)g_strdup(buf);
344                 *app_handle = list->app_handle;
345                 g_app_list = g_slist_append(g_app_list, list);
346         }
347
348         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
349
350         BT_DBG("-");
351         return result;
352 }
353
354 BT_EXPORT_API int bluetooth_hdp_deactivate(const char *app_handle)
355 {
356         int result;
357         hdp_app_list_t *list;
358
359         BT_DBG("+");
360
361         BT_CHECK_ENABLED(return);
362         BT_CHECK_PARAMETER(app_handle, return);
363
364         list = __bt_hdp_internal_gslist_find_app_handler((void *)app_handle);
365         if (NULL == list) {
366                 BT_ERR("**** list not found for %s ******\n", app_handle);
367                 return BLUETOOTH_ERROR_INVALID_PARAM;
368         }
369
370         BT_DBG("app_handle: %s", app_handle);
371
372         BT_INIT_PARAMS();
373         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
374
375         g_array_append_vals(in_param1, app_handle, (strlen(app_handle) + 1));
376
377         result = _bt_send_request(BT_BLUEZ_SERVICE, BT_HDP_UNREGISTER_SINK_APP,
378                 in_param1, in_param2, in_param3, in_param4, &out_param);
379         if (result == BLUETOOTH_ERROR_NONE) {
380                 g_app_list = g_slist_remove(g_app_list, list);
381                 g_free(list->app_handle);
382                 g_slist_foreach(list->obj_info, (GFunc)__bt_hdp_obj_info_free, NULL);
383                 g_free(list);
384         }
385
386         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
387
388         return result;
389 }
390
391 BT_EXPORT_API int bluetooth_hdp_send_data(unsigned int channel_id,
392                 const char *buffer,
393                 unsigned int size)
394 {
395         unsigned int wbytes = 0;
396         int written = 0;
397         hdp_obj_info_t *info;
398         int result;
399
400         BT_DBG("+");
401
402         BT_CHECK_ENABLED(return);
403
404         retv_if(NULL == buffer, BLUETOOTH_ERROR_INVALID_PARAM);
405         retv_if(0 == size, BLUETOOTH_ERROR_INVALID_PARAM);
406
407         info = __bt_hdp_internal_gslist_obj_find_using_ch_id(channel_id);
408         if (NULL == info) {
409                 BT_ERR("*** Could not locate the info for %d*****", channel_id);
410                 return BLUETOOTH_ERROR_INVALID_PARAM;
411         }
412
413         switch (privilege_token) {
414         case 0:
415                 result = _bt_check_privilege(BT_CHECK_PRIVILEGE, BT_HDP_SEND_DATA);
416
417                 if (result == BLUETOOTH_ERROR_NONE) {
418                         privilege_token = 1; /* Have a permission */
419                 } else if (result == BLUETOOTH_ERROR_PERMISSION_DEINED) {
420                         BT_ERR("Don't have a privilege to use this API");
421                         privilege_token = -1; /* Don't have a permission */
422                         return BLUETOOTH_ERROR_PERMISSION_DEINED;
423                 } else {
424                         /* Just break - It is not related with permission error */
425                 }
426                 break;
427         case 1:
428                 /* Already have a privilege */
429                 break;
430         case -1:
431                 return BLUETOOTH_ERROR_PERMISSION_DEINED;
432         default:
433                 /* Invalid privilge token value */
434                 return BLUETOOTH_ERROR_INTERNAL;
435         }
436
437         while (wbytes < size) {
438                 written = write(info->fd, (buffer + wbytes), (size - wbytes));
439                 if (written <= 0) {
440                         BT_ERR("write failed..\n");
441                         return BLUETOOTH_ERROR_NOT_IN_OPERATION;
442                 }
443                 wbytes += written;
444         }
445
446         return BLUETOOTH_ERROR_NONE;
447 }
448
449 BT_EXPORT_API int bluetooth_hdp_connect(const char *app_handle,
450                         bt_hdp_qos_type_t channel_type,
451                         const bluetooth_device_address_t *device_address)
452 {
453         int result;
454         hdp_app_list_t *list;
455         bt_user_info_t *user_info;
456
457         BT_CHECK_ENABLED(return);
458         BT_CHECK_PARAMETER(app_handle, return);
459         BT_CHECK_PARAMETER(device_address, return);
460
461         user_info = _bt_get_user_data(BT_COMMON);
462         retv_if(user_info->cb == NULL, BLUETOOTH_ERROR_INTERNAL);
463
464         list = __bt_hdp_internal_gslist_find_app_handler((void *)app_handle);
465         if (NULL == list) {
466                 BT_ERR("**** list not found for %s ******\n", app_handle);
467                 return BLUETOOTH_ERROR_INVALID_PARAM;
468         }
469
470         BT_DBG("app_handle: %s", app_handle);
471
472         BT_INIT_PARAMS();
473         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
474
475         g_array_append_vals(in_param1, app_handle, (strlen(app_handle) + 1));
476         g_array_append_vals(in_param2, &channel_type, sizeof(bt_hdp_qos_type_t));
477         g_array_append_vals(in_param3, device_address, sizeof(bluetooth_device_address_t));
478
479         result = _bt_send_request_async(BT_BLUEZ_SERVICE,
480                         BT_HDP_CONNECT, in_param1, in_param2, in_param3, in_param4,
481                         user_info->cb, user_info->user_data);
482
483         if (result != BLUETOOTH_ERROR_NONE)
484                 BT_ERR("BT_HDP_CONNECT failed");
485
486         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
487
488         return result;
489 }
490
491 BT_EXPORT_API int bluetooth_hdp_disconnect(unsigned int channel_id,
492                         const bluetooth_device_address_t *device_address)
493 {
494         int result;
495         hdp_obj_info_t *info;
496         bt_user_info_t *user_info;
497
498         BT_CHECK_ENABLED(return);
499         BT_CHECK_PARAMETER(device_address, return);
500
501         user_info = _bt_get_user_data(BT_COMMON);
502         retv_if(user_info->cb == NULL, BLUETOOTH_ERROR_INTERNAL);
503
504         BT_DBG("channel_id: %d", channel_id);
505         info = __bt_hdp_internal_gslist_obj_find_using_ch_id(channel_id);
506         if (NULL == info) {
507                 BT_ERR("*** Could not locate the info for %d*****", channel_id);
508                 return BLUETOOTH_ERROR_INVALID_PARAM;
509         }
510
511         BT_INIT_PARAMS();
512         BT_ALLOC_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
513
514         g_array_append_vals(in_param1, &channel_id, sizeof(int));
515         g_array_append_vals(in_param2, device_address, sizeof(bluetooth_device_address_t));
516
517         result = _bt_send_request_async(BT_BLUEZ_SERVICE, BT_HDP_DISCONNECT,
518                         in_param1, in_param2, in_param3, in_param4,
519                         user_info->cb, user_info->user_data);
520         if (result != BLUETOOTH_ERROR_NONE)
521                 BT_ERR("BT_HDP_DISCONNECT failed");
522
523         BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
524         return result;
525 }