TFIVE-12640: fixed the size of handle string
[platform/core/connectivity/bluetooth-frwk.git] / bt-service-adaptation / services / health / bt-service-hdp.c
1 /*
2  * Bluetooth-frwk
3  *
4  * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Author: Atul Rai <a.rai@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *              http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <gio/gunixfdlist.h>
25 #include <glib.h>
26 #include <dlog.h>
27
28 /* OAL headers */
29 #include <oal-event.h>
30 #include <oal-hdp.h>
31
32 /* bt-service headers */
33 #include "bt-internal-types.h"
34 #include "bt-service-common.h"
35 #include "bt-service-util.h"
36 #include "bt-service-event-receiver.h"
37 #include "bt-service-event.h"
38 #include "bt-service-hdp.h"
39
40 typedef struct {
41         int fd;
42         void *data;
43 } bt_hdp_conn_info_t;
44
45 typedef struct {
46         int app_id;
47         char *owner;
48 } bt_hdp_app_info_t;
49
50 static GSList *app_list;
51 static GSList *pending_conn_list;
52
53 static bt_hdp_app_info_t *__find_app_by_id(int app_id)
54 {
55         GSList *l;
56
57         for (l = app_list; NULL != l; l = g_slist_next(l)) {
58                 bt_hdp_app_info_t *app = l->data;
59                 if (app && (app->app_id == app_id))
60                         return app;
61         }
62
63         return NULL;
64 }
65
66 static void __bt_hdp_handle_pending_request_info(int result,
67                 int service_function, void *data, unsigned int size)
68 {
69         GSList *l;
70         GArray *out_param;
71         invocation_info_t *req_info = NULL;
72
73         ret_if(data == NULL);
74
75         BT_DBG("+");
76
77         for (l = _bt_get_invocation_list(); l != NULL; l = g_slist_next(l)) {
78                 req_info = l->data;
79                 if (req_info == NULL || req_info->service_function != service_function)
80                         continue;
81
82                 switch (service_function) {
83                 case BT_HDP_REGISTER_SINK_APP:
84                 case BT_HDP_UNREGISTER_SINK_APP: {
85                         int app_id = *((int *)data);
86                         char *app_handle;
87
88                         if (*((int *)req_info->user_data) != app_id)
89                                 break;
90
91                         app_handle = g_strdup_printf("health_app_%d", app_id);
92                         BT_DBG("app_handle: %s", app_handle);
93
94                         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
95                         g_array_append_vals(out_param, app_handle, strlen(app_handle));
96                         _bt_service_method_return(req_info->context, out_param, result);
97                         g_free(req_info->user_data);
98                         _bt_free_info_from_invocation_list(req_info);
99                         g_array_free(out_param, TRUE);
100                         g_free(app_handle);
101                         break;
102                 }
103                 case BT_HDP_CONNECT: {
104                         bt_hdp_conn_info_t *info = data;
105                         bt_hdp_connected_t *conn_data = info->data;
106                         bt_hdp_connected_t *req_conn_data = req_info->user_data;
107
108                         if (!conn_data)
109                                 break;
110
111                         if (req_conn_data->channel_id != conn_data->channel_id)
112                                 break;
113
114                         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
115                         g_array_append_vals(out_param, conn_data, sizeof(bt_hdp_connected_t));
116
117                         _bt_service_method_return(req_info->context, out_param, result);
118                         g_free(req_info->user_data);
119                         _bt_free_info_from_invocation_list(req_info);
120                         g_array_free(out_param, TRUE);
121                         break;
122                 }
123                 case BT_HDP_DISCONNECT: {
124                         bt_hdp_disconnected_t *disconn_data = data;
125                         bt_hdp_disconnected_t *req_disconn_data = req_info->user_data;
126
127                         if (req_disconn_data->channel_id != disconn_data->channel_id)
128                                 break;
129
130                         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
131                         g_array_append_vals(out_param, disconn_data, sizeof(bt_hdp_disconnected_t));
132                         _bt_service_method_return(req_info->context, out_param, result);
133                         g_free(req_info->user_data);
134                         _bt_free_info_from_invocation_list(req_info);
135                         g_array_free(out_param, TRUE);
136                         break;
137                 }
138                 case BT_HDP_GET_FD: {
139                         GUnixFDList *fd_list = NULL;
140
141                         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
142                         if (BLUETOOTH_ERROR_NONE == result) {
143                                 GError *error = NULL;
144                                 bt_hdp_conn_info_t *info = data;
145                                 bt_hdp_connected_t *conn_data = info->data;
146
147                                 if (*((int *)req_info->user_data) != (int)conn_data->channel_id)
148                                         break;
149
150                                 BT_DBG("fd: %d", info->fd);
151
152                                 fd_list = g_unix_fd_list_new();
153                                 g_unix_fd_list_append(fd_list, info->fd, &error);
154                                 g_assert_no_error(error);
155                                 close(info->fd);
156                         }
157
158                         _bt_service_method_return_with_unix_fd_list(
159                                         req_info->context, out_param, result, fd_list);
160                         if (fd_list)
161                                 g_object_unref(fd_list);
162                         g_free(req_info->user_data);
163                         _bt_free_info_from_invocation_list(req_info);
164                         g_array_free(out_param, TRUE);
165                         return;
166                 }
167                 default:
168                         BT_ERR("Unknown service function");
169                         break;
170                 }
171         }
172
173         BT_DBG("-");
174 }
175
176 static bt_hdp_qos_type_t __convert_oal_ch_type_to_hdp_qos_type(int channel_type)
177 {
178         switch (channel_type) {
179         case OAL_CHANNEL_TYPE_RELIABLE:
180                 return HDP_QOS_RELIABLE;
181         case OAL_CHANNEL_TYPE_STREAMING:
182                 return HDP_QOS_STREAMING;
183         default:
184                 return HDP_QOS_ANY;
185         }
186 }
187
188 static void __handle_hdp_channel_connected(event_hdp_channel_conn_t *event)
189 {
190         GVariant *param;
191         bt_hdp_app_info_t *app;
192         bt_hdp_conn_info_t *hdp_conn;
193         bt_hdp_connected_t *conn_info;
194         char address[BT_ADDRESS_STRING_SIZE];
195         char *app_handle;
196
197         BT_DBG("+");
198
199         app = __find_app_by_id(event->app_id);
200         ret_if(NULL == app);
201
202         app_handle = g_strdup_printf("health_app_%d", event->app_id);
203
204         conn_info = g_new0(bt_hdp_connected_t, 1);
205         conn_info->type = __convert_oal_ch_type_to_hdp_qos_type(event->ch_type);
206         conn_info->channel_id = event->channel_id;
207         conn_info->app_handle = app_handle;
208         memcpy(conn_info->device_address.addr, event->address.addr, BLUETOOTH_ADDRESS_LENGTH);
209
210         hdp_conn = g_new0(bt_hdp_conn_info_t, 1);
211         hdp_conn->fd = event->fd;
212         hdp_conn->data = conn_info;
213         pending_conn_list = g_slist_append(pending_conn_list, hdp_conn);
214
215         __bt_hdp_handle_pending_request_info(BLUETOOTH_ERROR_NONE,
216                         BT_HDP_CONNECT, (void *)hdp_conn, sizeof(bt_hdp_conn_info_t));
217
218         _bt_convert_addr_type_to_string(address, event->address.addr);
219         BT_DBG("HDP connected to %s, app_handle: %s", address, conn_info->app_handle);
220
221         param = g_variant_new("(issui)",
222                         BLUETOOTH_ERROR_NONE,
223                         address, conn_info->app_handle,
224                         conn_info->channel_id, conn_info->type);
225         _bt_send_event_to_dest(app->owner,
226                 BT_HDP_EVENT, BLUETOOTH_EVENT_HDP_CONNECTED, param);
227
228         g_free(app_handle);
229
230         BT_DBG("-");
231 }
232
233 static void __handle_hdp_channel_disconnected(event_hdp_channel_conn_t *event)
234 {
235         bt_hdp_app_info_t *app;
236
237
238         BT_DBG("+");
239
240         app = __find_app_by_id(event->app_id);
241         ret_if(NULL == app);
242
243         /* reply to pending requests if any */
244         if (0 > event->fd) {
245                 bt_hdp_conn_info_t hdp_conn;
246                 bt_hdp_connected_t conn_info;
247                 char *app_handle;
248
249                 BT_DBG("Connect request failed for channel_id: %d", event->channel_id);
250                 memset(&conn_info, 0x00, sizeof(bt_hdp_connected_t));
251                 conn_info.type = __convert_oal_ch_type_to_hdp_qos_type(event->ch_type);
252                 conn_info.channel_id = event->channel_id;
253
254                 app_handle = g_strdup_printf("health_app_%d", event->app_id);
255                 conn_info.app_handle = app_handle;
256
257                 memcpy(conn_info.device_address.addr, event->address.addr, BLUETOOTH_ADDRESS_LENGTH);
258
259                 hdp_conn.fd = event->fd;
260                 hdp_conn.data = &conn_info;
261
262                 __bt_hdp_handle_pending_request_info(BLUETOOTH_ERROR_INTERNAL,
263                                 BT_HDP_CONNECT, (void *)&hdp_conn, sizeof(bt_hdp_conn_info_t));
264
265                 g_free(app_handle);
266         } else {
267                 bt_hdp_disconnected_t disconn_info;
268                 char address[BT_ADDRESS_STRING_SIZE];
269                 char *app_handle;
270                 GVariant *param;
271
272                 memset(&disconn_info, 0x00, sizeof(bt_hdp_disconnected_t));
273                 disconn_info.channel_id = event->channel_id;
274                 memcpy(disconn_info.device_address.addr, event->address.addr, BLUETOOTH_ADDRESS_LENGTH);
275
276                 __bt_hdp_handle_pending_request_info(BLUETOOTH_ERROR_NONE,
277                                 BT_HDP_DISCONNECT, (void *)&disconn_info, sizeof(bt_hdp_disconnected_t));
278
279                 /* Send event to application */
280                 _bt_convert_addr_type_to_string(address, event->address.addr);
281                 app_handle = g_strdup_printf("health_app_%d", event->app_id);
282
283                 BT_DBG("HDP disconnected from %s, app_handle: %s", address, app_handle);
284                 param = g_variant_new("(isu)",
285                                 BLUETOOTH_ERROR_NONE,
286                                 address, event->channel_id);
287                 _bt_send_event_to_dest(app->owner,
288                                 BT_HDP_EVENT, BLUETOOTH_EVENT_HDP_DISCONNECTED, param);
289
290                 g_free(app_handle);
291         }
292
293         BT_DBG("-");
294 }
295
296 static void __bt_hdp_event_handler(int event_type, gpointer event_data)
297 {
298         BT_INFO("OAL event = 0x%x, \n", event_type);
299
300         switch (event_type) {
301         case OAL_EVENT_HDP_APP_REGISTERED: {
302                 event_hdp_app_reg_t *app_data = event_data;
303                 int result;
304                 int app_id;
305
306                 ret_if(NULL == event_data);
307                 app_id = app_data->app_id;
308                 if (OAL_STATUS_SUCCESS != app_data->status) {
309                         bt_hdp_app_info_t *app = __find_app_by_id(app_id);
310                         if (app) {
311                                 app_list = g_slist_remove(app_list, app);
312                                 g_free(app->owner);
313                                 g_free(app);
314                         }
315                         result = BLUETOOTH_ERROR_INTERNAL;
316                 } else {
317                         result = BLUETOOTH_ERROR_NONE;
318                 }
319
320                 BT_DBG("app_id: %d, resut: %d", app_id, result);
321                 __bt_hdp_handle_pending_request_info(result,
322                         BT_HDP_REGISTER_SINK_APP, (void *)&app_id, sizeof(app_id));
323                 break;
324         }
325         case OAL_EVENT_HDP_APP_UNREGISTERED: {
326                 event_hdp_app_reg_t *app_data = event_data;
327                 int result;
328                 int app_id;
329
330                 ret_if(NULL == event_data);
331                 app_id = app_data->app_id;
332                 if (OAL_STATUS_SUCCESS != app_data->status) {
333                         result = BLUETOOTH_ERROR_INTERNAL;
334                 } else {
335                         bt_hdp_app_info_t *app = __find_app_by_id(app_id);
336                         if (app) {
337                                 app_list = g_slist_remove(app_list, app);
338                                 g_free(app->owner);
339                                 g_free(app);
340                         }
341                         result = BLUETOOTH_ERROR_NONE;
342                 }
343
344                 BT_DBG("app_id: %d, resut: %d", app_id, result);
345                 __bt_hdp_handle_pending_request_info(result,
346                         BT_HDP_UNREGISTER_SINK_APP, (void *)&app_id, sizeof(app_id));
347                 break;
348         }
349         case OAL_EVENT_HDP_CHANNEL_CONNECTED: {
350                 ret_if(NULL == event_data);
351                 __handle_hdp_channel_connected((event_hdp_channel_conn_t *)event_data);
352                 break;
353         }
354         case OAL_EVENT_HDP_CHANNEL_DESTROYED: {
355                 ret_if(NULL == event_data);
356                 __handle_hdp_channel_disconnected((event_hdp_channel_conn_t *)event_data);
357                 break;
358         }
359         default:
360                 BT_ERR("Invalid event:%d\n", event_type);
361                 break;
362         }
363 }
364
365 int _bt_hdp_init(void)
366 {
367         BT_DBG("+");
368
369         if (OAL_STATUS_SUCCESS != hdp_init()) {
370                 BT_ERR("hdp_init failed");
371                 return BLUETOOTH_ERROR_INTERNAL;
372         }
373
374         /* Register SOCKET event handler */
375         _bt_service_register_event_handler_callback(BT_HEALTH_MODULE, __bt_hdp_event_handler);
376         BT_DBG("-");
377         return BLUETOOTH_ERROR_NONE;
378 }
379
380 void _bt_hdp_deinit(void)
381 {
382         BT_DBG("+");
383
384         if (OAL_STATUS_SUCCESS != hdp_cleanup())
385                 BT_ERR("hdp_cleanup failed");
386
387         /* Un-register SOCKET event handler */
388         _bt_service_unregister_event_handler_callback(BT_HEALTH_MODULE);
389         BT_DBG("-");
390 }
391
392 int _bt_hdp_app_register(bt_hdp_role_type_t role, bt_hdp_qos_type_t channel_type,
393                 unsigned short data_type, char *sender, int *app_id)
394 {
395         oal_channel_type_t ch_type;
396         oal_hdp_role_t dev_role;
397         bt_hdp_app_info_t *app;
398
399         BT_DBG("+");
400
401         retv_if(NULL == app_id, BLUETOOTH_ERROR_INVALID_PARAM);
402
403         switch (role) {
404         case HDP_ROLE_SOURCE:
405                 dev_role = OAL_HDP_ROLE_SOURCE;
406                 break;
407         case HDP_ROLE_SINK:
408                 dev_role = OAL_HDP_ROLE_SINK;
409                 break;
410         default:
411                 BT_ERR("Invalid HDP role");
412                 return BLUETOOTH_ERROR_INVALID_PARAM;
413         }
414
415         switch (channel_type) {
416         case HDP_QOS_RELIABLE:
417                 ch_type = OAL_CHANNEL_TYPE_RELIABLE;
418                 break;
419         case HDP_QOS_STREAMING:
420                 ch_type = OAL_CHANNEL_TYPE_STREAMING;
421                 break;
422         case HDP_QOS_ANY:
423                 ch_type = OAL_CHANNEL_TYPE_ANY;
424                 break;
425         default:
426                 BT_ERR("Invalid channel type");
427                 return BLUETOOTH_ERROR_INVALID_PARAM;
428         }
429
430         *app_id = hdp_register_application(dev_role, ch_type, sender, data_type);
431         if (0 > *app_id) {
432                 BT_ERR("hdp_register_application failed");
433                 return BLUETOOTH_ERROR_INTERNAL;
434         }
435
436         /* Add application to the list */
437         app = g_new0(bt_hdp_app_info_t, 1);
438         app->app_id = *app_id;
439         app->owner = g_strdup(sender);
440         app_list = g_slist_append(app_list, app);
441
442         BT_DBG("-");
443         return BLUETOOTH_ERROR_NONE;
444 }
445
446 int _bt_hdp_app_unregister(int app_id)
447 {
448         int ret;
449
450         BT_DBG("+");
451
452         ret = hdp_unregister_application(app_id);
453         if (OAL_STATUS_SUCCESS != ret) {
454                 BT_ERR("hdp_unregister_application failed");
455                 return BLUETOOTH_ERROR_INTERNAL;
456         }
457
458         BT_DBG("-");
459         return BLUETOOTH_ERROR_NONE;
460 }
461
462 int _bt_hdp_connect(int app_id, bluetooth_device_address_t *address,
463                 bt_hdp_qos_type_t channel_type, int *channel_id)
464 {
465         bt_address_t bd_addr;
466
467         BT_DBG("+");
468
469         retv_if(NULL == address, BLUETOOTH_ERROR_INVALID_PARAM);
470         retv_if(NULL == channel_id, BLUETOOTH_ERROR_INVALID_PARAM);
471
472         memset(&bd_addr, 0x00, sizeof(bt_address_t));
473         memcpy(bd_addr.addr, address->addr, BT_ADDRESS_BYTES_NUM);
474
475         *channel_id = hdp_connect_channel(app_id, &bd_addr);
476         if (0 > *channel_id) {
477                 BT_ERR("hdp_connect_channel failed");
478                 return BLUETOOTH_ERROR_INTERNAL;
479         }
480
481         BT_DBG("-");
482         return BLUETOOTH_ERROR_NONE;
483 }
484
485 int _bt_hdp_disconnect(int channel_id)
486 {
487         int ret;
488
489         BT_DBG("+");
490
491         ret = hdp_disconnect_channel(channel_id);
492         if (OAL_STATUS_SUCCESS != ret) {
493                 BT_ERR("hdp_disconnect_channel failed");
494                 return BLUETOOTH_ERROR_INTERNAL;
495         }
496
497         BT_DBG("-");
498         return BLUETOOTH_ERROR_NONE;
499 }
500
501 gboolean __send_fd(gpointer data)
502 {
503         bt_hdp_conn_info_t *info = data;
504
505         BT_DBG("+");
506
507         if (info) {
508                 pending_conn_list = g_slist_remove(pending_conn_list, info);
509                 __bt_hdp_handle_pending_request_info(BLUETOOTH_ERROR_NONE,
510                                 BT_HDP_GET_FD, (void *)info, sizeof(bt_hdp_conn_info_t));
511                 g_free(info->data);
512                 g_free(info);
513         } else {
514                 __bt_hdp_handle_pending_request_info(
515                                 BLUETOOTH_ERROR_INTERNAL, BT_HDP_GET_FD, NULL, 0);
516         }
517
518         BT_DBG("-");
519         return FALSE;
520 }
521
522 int _bt_hdp_get_fd(int channel_id)
523 {
524         GSList *l;
525         bt_hdp_conn_info_t *info = NULL;
526         bt_hdp_connected_t *conn_info = NULL;
527
528         BT_DBG("+");
529
530         BT_DBG("channel_id: %d, pending_conn_list: %p", channel_id, pending_conn_list);
531         for (l = pending_conn_list; NULL != l; l = g_slist_next(l)) {
532                 info = l->data;
533
534                 if (!info)
535                         continue;
536
537                 conn_info = (bt_hdp_connected_t *)(info->data);
538                 if (conn_info && ((int)conn_info->channel_id == channel_id)) {
539                         BT_DBG("Match found");
540                         break;
541                 }
542
543                 info = NULL;
544                 conn_info = NULL;
545         }
546
547         g_idle_add(__send_fd, info);
548         BT_DBG("-");
549         return BLUETOOTH_ERROR_NONE;
550 }
551
552 void _bt_check_hdp_app_termination(const char *name)
553 {
554         GSList *l;
555         bt_hdp_app_info_t *app = NULL;
556
557         ret_if(NULL == name);
558
559         for (l = app_list; NULL != l; l = g_slist_next(l)) {
560                 app = l->data;
561
562                 if (!app)
563                         continue;
564
565                 if (app->owner && !strncasecmp(app->owner, name, strlen(name))) {
566                         BT_DBG("Match found, name: %s", name);
567                         _bt_hdp_app_unregister(app->app_id);
568                 }
569         }
570 }