76ed9e856184dcd9a52a005da4191593c4f158e4
[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; ) {
78                 req_info = l->data;
79                 l = g_slist_next(l);
80                 if (req_info == NULL || req_info->service_function != service_function)
81                         continue;
82
83                 switch (service_function) {
84                 case BT_HDP_REGISTER_SINK_APP:
85                 case BT_HDP_UNREGISTER_SINK_APP: {
86                         int app_id = *((int *)data);
87                         char *app_handle;
88
89                         if (*((int *)req_info->user_data) != app_id)
90                                 break;
91
92                         app_handle = g_strdup_printf("health_app_%d", app_id);
93                         BT_DBG("app_handle: %s", app_handle);
94
95                         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
96                         g_array_append_vals(out_param, app_handle, strlen(app_handle));
97                         _bt_service_method_return(req_info->context, out_param, result);
98                         g_free(req_info->user_data);
99                         _bt_free_info_from_invocation_list(req_info);
100                         g_array_free(out_param, TRUE);
101                         g_free(app_handle);
102                         break;
103                 }
104                 case BT_HDP_CONNECT: {
105                         bt_hdp_conn_info_t *info = data;
106                         bt_hdp_connected_t *conn_data = info->data;
107                         bt_hdp_connected_t *req_conn_data = req_info->user_data;
108
109                         if (!conn_data)
110                                 break;
111
112                         if (req_conn_data->channel_id != conn_data->channel_id)
113                                 break;
114
115                         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
116                         g_array_append_vals(out_param, conn_data, sizeof(bt_hdp_connected_t));
117
118                         _bt_service_method_return(req_info->context, out_param, result);
119                         g_free(req_info->user_data);
120                         _bt_free_info_from_invocation_list(req_info);
121                         g_array_free(out_param, TRUE);
122                         break;
123                 }
124                 case BT_HDP_DISCONNECT: {
125                         bt_hdp_disconnected_t *disconn_data = data;
126                         bt_hdp_disconnected_t *req_disconn_data = req_info->user_data;
127
128                         if (req_disconn_data->channel_id != disconn_data->channel_id)
129                                 break;
130
131                         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
132                         g_array_append_vals(out_param, disconn_data, sizeof(bt_hdp_disconnected_t));
133                         _bt_service_method_return(req_info->context, out_param, result);
134                         g_free(req_info->user_data);
135                         _bt_free_info_from_invocation_list(req_info);
136                         g_array_free(out_param, TRUE);
137                         break;
138                 }
139                 case BT_HDP_GET_FD: {
140                         GUnixFDList *fd_list = NULL;
141
142                         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
143                         if (BLUETOOTH_ERROR_NONE == result) {
144                                 GError *error = NULL;
145                                 bt_hdp_conn_info_t *info = data;
146                                 bt_hdp_connected_t *conn_data = info->data;
147
148                                 if (*((int *)req_info->user_data) != (int)conn_data->channel_id)
149                                         break;
150
151                                 BT_DBG("fd: %d", info->fd);
152
153                                 fd_list = g_unix_fd_list_new();
154                                 g_unix_fd_list_append(fd_list, info->fd, &error);
155                                 g_assert_no_error(error);
156                                 close(info->fd);
157                         }
158
159                         _bt_service_method_return_with_unix_fd_list(
160                                         req_info->context, out_param, result, fd_list);
161                         if (fd_list)
162                                 g_object_unref(fd_list);
163                         g_free(req_info->user_data);
164                         _bt_free_info_from_invocation_list(req_info);
165                         g_array_free(out_param, TRUE);
166                         return;
167                 }
168                 default:
169                         BT_ERR("Unknown service function");
170                         break;
171                 }
172         }
173
174         BT_DBG("-");
175 }
176
177 static bt_hdp_qos_type_t __convert_oal_ch_type_to_hdp_qos_type(int channel_type)
178 {
179         switch (channel_type) {
180         case OAL_CHANNEL_TYPE_RELIABLE:
181                 return HDP_QOS_RELIABLE;
182         case OAL_CHANNEL_TYPE_STREAMING:
183                 return HDP_QOS_STREAMING;
184         default:
185                 return HDP_QOS_ANY;
186         }
187 }
188
189 static void __handle_hdp_channel_connected(event_hdp_channel_conn_t *event)
190 {
191         GVariant *param;
192         bt_hdp_app_info_t *app;
193         bt_hdp_conn_info_t *hdp_conn;
194         bt_hdp_connected_t *conn_info;
195         char address[BT_ADDRESS_STRING_SIZE];
196         char *app_handle;
197
198         BT_DBG("+");
199
200         app = __find_app_by_id(event->app_id);
201         ret_if(NULL == app);
202
203         app_handle = g_strdup_printf("health_app_%d", event->app_id);
204
205         conn_info = g_new0(bt_hdp_connected_t, 1);
206         conn_info->type = __convert_oal_ch_type_to_hdp_qos_type(event->ch_type);
207         conn_info->channel_id = event->channel_id;
208         conn_info->app_handle = app_handle;
209         memcpy(conn_info->device_address.addr, event->address.addr, BLUETOOTH_ADDRESS_LENGTH);
210
211         hdp_conn = g_new0(bt_hdp_conn_info_t, 1);
212         hdp_conn->fd = event->fd;
213         hdp_conn->data = conn_info;
214         pending_conn_list = g_slist_append(pending_conn_list, hdp_conn);
215
216         __bt_hdp_handle_pending_request_info(BLUETOOTH_ERROR_NONE,
217                         BT_HDP_CONNECT, (void *)hdp_conn, sizeof(bt_hdp_conn_info_t));
218
219         _bt_convert_addr_type_to_string(address, event->address.addr);
220         BT_DBG("HDP connected to %s, app_handle: %s", address, conn_info->app_handle);
221
222         param = g_variant_new("(issui)",
223                         BLUETOOTH_ERROR_NONE,
224                         address, conn_info->app_handle,
225                         conn_info->channel_id, conn_info->type);
226         _bt_send_event_to_dest(app->owner,
227                 BT_HDP_EVENT, BLUETOOTH_EVENT_HDP_CONNECTED, param);
228
229         g_free(app_handle);
230
231         BT_DBG("-");
232 }
233
234 static void __handle_hdp_channel_disconnected(event_hdp_channel_conn_t *event)
235 {
236         bt_hdp_app_info_t *app;
237
238
239         BT_DBG("+");
240
241         app = __find_app_by_id(event->app_id);
242         ret_if(NULL == app);
243
244         /* reply to pending requests if any */
245         if (0 > event->fd) {
246                 bt_hdp_conn_info_t hdp_conn;
247                 bt_hdp_connected_t conn_info;
248                 char *app_handle;
249
250                 BT_DBG("Connect request failed for channel_id: %d", event->channel_id);
251                 memset(&conn_info, 0x00, sizeof(bt_hdp_connected_t));
252                 conn_info.type = __convert_oal_ch_type_to_hdp_qos_type(event->ch_type);
253                 conn_info.channel_id = event->channel_id;
254
255                 app_handle = g_strdup_printf("health_app_%d", event->app_id);
256                 conn_info.app_handle = app_handle;
257
258                 memcpy(conn_info.device_address.addr, event->address.addr, BLUETOOTH_ADDRESS_LENGTH);
259
260                 hdp_conn.fd = event->fd;
261                 hdp_conn.data = &conn_info;
262
263                 __bt_hdp_handle_pending_request_info(BLUETOOTH_ERROR_INTERNAL,
264                                 BT_HDP_CONNECT, (void *)&hdp_conn, sizeof(bt_hdp_conn_info_t));
265
266                 g_free(app_handle);
267         } else {
268                 bt_hdp_disconnected_t disconn_info;
269                 char address[BT_ADDRESS_STRING_SIZE];
270                 char *app_handle;
271                 GVariant *param;
272
273                 memset(&disconn_info, 0x00, sizeof(bt_hdp_disconnected_t));
274                 disconn_info.channel_id = event->channel_id;
275                 memcpy(disconn_info.device_address.addr, event->address.addr, BLUETOOTH_ADDRESS_LENGTH);
276
277                 __bt_hdp_handle_pending_request_info(BLUETOOTH_ERROR_NONE,
278                                 BT_HDP_DISCONNECT, (void *)&disconn_info, sizeof(bt_hdp_disconnected_t));
279
280                 /* Send event to application */
281                 _bt_convert_addr_type_to_string(address, event->address.addr);
282                 app_handle = g_strdup_printf("health_app_%d", event->app_id);
283
284                 BT_DBG("HDP disconnected from %s, app_handle: %s", address, app_handle);
285                 param = g_variant_new("(isu)",
286                                 BLUETOOTH_ERROR_NONE,
287                                 address, event->channel_id);
288                 _bt_send_event_to_dest(app->owner,
289                                 BT_HDP_EVENT, BLUETOOTH_EVENT_HDP_DISCONNECTED, param);
290
291                 g_free(app_handle);
292         }
293
294         BT_DBG("-");
295 }
296
297 static void __bt_hdp_event_handler(int event_type, gpointer event_data)
298 {
299         BT_INFO("OAL event = 0x%x, \n", event_type);
300
301         switch (event_type) {
302         case OAL_EVENT_HDP_APP_REGISTERED: {
303                 event_hdp_app_reg_t *app_data = event_data;
304                 int result;
305                 int app_id;
306
307                 ret_if(NULL == event_data);
308                 app_id = app_data->app_id;
309                 if (OAL_STATUS_SUCCESS != app_data->status) {
310                         bt_hdp_app_info_t *app = __find_app_by_id(app_id);
311                         if (app) {
312                                 app_list = g_slist_remove(app_list, app);
313                                 g_free(app->owner);
314                                 g_free(app);
315                         }
316                         result = BLUETOOTH_ERROR_INTERNAL;
317                 } else {
318                         result = BLUETOOTH_ERROR_NONE;
319                 }
320
321                 BT_DBG("app_id: %d, resut: %d", app_id, result);
322                 __bt_hdp_handle_pending_request_info(result,
323                         BT_HDP_REGISTER_SINK_APP, (void *)&app_id, sizeof(app_id));
324                 break;
325         }
326         case OAL_EVENT_HDP_APP_UNREGISTERED: {
327                 event_hdp_app_reg_t *app_data = event_data;
328                 int result;
329                 int app_id;
330
331                 ret_if(NULL == event_data);
332                 app_id = app_data->app_id;
333                 if (OAL_STATUS_SUCCESS != app_data->status) {
334                         result = BLUETOOTH_ERROR_INTERNAL;
335                 } else {
336                         bt_hdp_app_info_t *app = __find_app_by_id(app_id);
337                         if (app) {
338                                 app_list = g_slist_remove(app_list, app);
339                                 g_free(app->owner);
340                                 g_free(app);
341                         }
342                         result = BLUETOOTH_ERROR_NONE;
343                 }
344
345                 BT_DBG("app_id: %d, resut: %d", app_id, result);
346                 __bt_hdp_handle_pending_request_info(result,
347                         BT_HDP_UNREGISTER_SINK_APP, (void *)&app_id, sizeof(app_id));
348                 break;
349         }
350         case OAL_EVENT_HDP_CHANNEL_CONNECTED: {
351                 ret_if(NULL == event_data);
352                 __handle_hdp_channel_connected((event_hdp_channel_conn_t *)event_data);
353                 break;
354         }
355         case OAL_EVENT_HDP_CHANNEL_DESTROYED: {
356                 ret_if(NULL == event_data);
357                 __handle_hdp_channel_disconnected((event_hdp_channel_conn_t *)event_data);
358                 break;
359         }
360         default:
361                 BT_ERR("Invalid event:%d\n", event_type);
362                 break;
363         }
364 }
365
366 int _bt_hdp_init(void)
367 {
368         BT_DBG("+");
369
370         if (OAL_STATUS_SUCCESS != hdp_init()) {
371                 BT_ERR("hdp_init failed");
372                 return BLUETOOTH_ERROR_INTERNAL;
373         }
374
375         /* Register SOCKET event handler */
376         _bt_service_register_event_handler_callback(BT_HEALTH_MODULE, __bt_hdp_event_handler);
377         BT_DBG("-");
378         return BLUETOOTH_ERROR_NONE;
379 }
380
381 void _bt_hdp_deinit(void)
382 {
383         BT_DBG("+");
384
385         if (OAL_STATUS_SUCCESS != hdp_cleanup())
386                 BT_ERR("hdp_cleanup failed");
387
388         /* Un-register SOCKET event handler */
389         _bt_service_unregister_event_handler_callback(BT_HEALTH_MODULE);
390         BT_DBG("-");
391 }
392
393 int _bt_hdp_app_register(bt_hdp_role_type_t role, bt_hdp_qos_type_t channel_type,
394                 unsigned short data_type, char *sender, int *app_id)
395 {
396         oal_channel_type_t ch_type;
397         oal_hdp_role_t dev_role;
398         bt_hdp_app_info_t *app;
399
400         BT_DBG("+");
401
402         retv_if(NULL == app_id, BLUETOOTH_ERROR_INVALID_PARAM);
403
404         switch (role) {
405         case HDP_ROLE_SOURCE:
406                 dev_role = OAL_HDP_ROLE_SOURCE;
407                 break;
408         case HDP_ROLE_SINK:
409                 dev_role = OAL_HDP_ROLE_SINK;
410                 break;
411         default:
412                 BT_ERR("Invalid HDP role");
413                 return BLUETOOTH_ERROR_INVALID_PARAM;
414         }
415
416         switch (channel_type) {
417         case HDP_QOS_RELIABLE:
418                 ch_type = OAL_CHANNEL_TYPE_RELIABLE;
419                 break;
420         case HDP_QOS_STREAMING:
421                 ch_type = OAL_CHANNEL_TYPE_STREAMING;
422                 break;
423         case HDP_QOS_ANY:
424                 ch_type = OAL_CHANNEL_TYPE_ANY;
425                 break;
426         default:
427                 BT_ERR("Invalid channel type");
428                 return BLUETOOTH_ERROR_INVALID_PARAM;
429         }
430
431         *app_id = hdp_register_application(dev_role, ch_type, sender, data_type);
432         if (0 > *app_id) {
433                 BT_ERR("hdp_register_application failed");
434                 return BLUETOOTH_ERROR_INTERNAL;
435         }
436
437         /* Add application to the list */
438         app = g_new0(bt_hdp_app_info_t, 1);
439         app->app_id = *app_id;
440         app->owner = g_strdup(sender);
441         app_list = g_slist_append(app_list, app);
442
443         BT_DBG("-");
444         return BLUETOOTH_ERROR_NONE;
445 }
446
447 int _bt_hdp_app_unregister(int app_id)
448 {
449         int ret;
450
451         BT_DBG("+");
452
453         ret = hdp_unregister_application(app_id);
454         if (OAL_STATUS_SUCCESS != ret) {
455                 BT_ERR("hdp_unregister_application failed");
456                 return BLUETOOTH_ERROR_INTERNAL;
457         }
458
459         BT_DBG("-");
460         return BLUETOOTH_ERROR_NONE;
461 }
462
463 int _bt_hdp_connect(int app_id, bluetooth_device_address_t *address,
464                 bt_hdp_qos_type_t channel_type, int *channel_id)
465 {
466         bt_address_t bd_addr;
467
468         BT_DBG("+");
469
470         retv_if(NULL == address, BLUETOOTH_ERROR_INVALID_PARAM);
471         retv_if(NULL == channel_id, BLUETOOTH_ERROR_INVALID_PARAM);
472
473         memset(&bd_addr, 0x00, sizeof(bt_address_t));
474         memcpy(bd_addr.addr, address->addr, BT_ADDRESS_BYTES_NUM);
475
476         *channel_id = hdp_connect_channel(app_id, &bd_addr);
477         if (0 > *channel_id) {
478                 BT_ERR("hdp_connect_channel failed");
479                 return BLUETOOTH_ERROR_INTERNAL;
480         }
481
482         BT_DBG("-");
483         return BLUETOOTH_ERROR_NONE;
484 }
485
486 int _bt_hdp_disconnect(int channel_id)
487 {
488         int ret;
489
490         BT_DBG("+");
491
492         ret = hdp_disconnect_channel(channel_id);
493         if (OAL_STATUS_SUCCESS != ret) {
494                 BT_ERR("hdp_disconnect_channel failed");
495                 return BLUETOOTH_ERROR_INTERNAL;
496         }
497
498         BT_DBG("-");
499         return BLUETOOTH_ERROR_NONE;
500 }
501
502 gboolean __send_fd(gpointer data)
503 {
504         bt_hdp_conn_info_t *info = data;
505
506         BT_DBG("+");
507
508         if (info) {
509                 pending_conn_list = g_slist_remove(pending_conn_list, info);
510                 __bt_hdp_handle_pending_request_info(BLUETOOTH_ERROR_NONE,
511                                 BT_HDP_GET_FD, (void *)info, sizeof(bt_hdp_conn_info_t));
512                 g_free(info->data);
513                 g_free(info);
514         } else {
515                 __bt_hdp_handle_pending_request_info(
516                                 BLUETOOTH_ERROR_INTERNAL, BT_HDP_GET_FD, NULL, 0);
517         }
518
519         BT_DBG("-");
520         return FALSE;
521 }
522
523 int _bt_hdp_get_fd(int channel_id)
524 {
525         GSList *l;
526         bt_hdp_conn_info_t *info = NULL;
527         bt_hdp_connected_t *conn_info = NULL;
528
529         BT_DBG("+");
530
531         BT_DBG("channel_id: %d, pending_conn_list: %p", channel_id, pending_conn_list);
532         for (l = pending_conn_list; NULL != l; l = g_slist_next(l)) {
533                 info = l->data;
534
535                 if (!info)
536                         continue;
537
538                 conn_info = (bt_hdp_connected_t *)(info->data);
539                 if (conn_info && ((int)conn_info->channel_id == channel_id)) {
540                         BT_DBG("Match found");
541                         break;
542                 }
543
544                 info = NULL;
545                 conn_info = NULL;
546         }
547
548         g_idle_add(__send_fd, info);
549         BT_DBG("-");
550         return BLUETOOTH_ERROR_NONE;
551 }
552
553 void _bt_check_hdp_app_termination(const char *name)
554 {
555         GSList *l;
556         bt_hdp_app_info_t *app = NULL;
557
558         ret_if(NULL == name);
559
560         for (l = app_list; NULL != l; l = g_slist_next(l)) {
561                 app = l->data;
562
563                 if (!app)
564                         continue;
565
566                 if (app->owner && !strncasecmp(app->owner, name, strlen(name))) {
567                         BT_DBG("Match found, name: %s", name);
568                         _bt_hdp_app_unregister(app->app_id);
569                 }
570         }
571 }