Simplify the logic to manage invocation list
[platform/core/connectivity/bluetooth-frwk.git] / bt-service-adaptation / services / audio / a2dp_sink / bt-service-a2dp-sink.c
1 /*
2  * Bluetooth-frwk
3  *
4  * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:  Nilesh Trimbake <t.shripati@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
22 #include <sys/socket.h>
23 #include <sys/errno.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <vconf.h>
27
28 #include "oal-hardware.h"
29 #include "oal-device-mgr.h"
30 #include <oal-manager.h>
31 #include <oal-a2dp-sink.h>
32
33 #include <bt-service-a2dp-sink.h>
34 #include <bt-service-event.h>
35
36 int _bt_a2dp_connect_remote_source(bluetooth_device_address_t *device_address/*,
37                                                 bt_audio_function_data_t *func_data*/)
38 {
39         oal_status_t status = OAL_STATUS_SUCCESS;
40         int result = BLUETOOTH_ERROR_NONE;
41         BT_INFO("+");
42
43         status = a2dp_sink_connect((bt_address_t*)device_address);
44         if (status != OAL_STATUS_SUCCESS) {
45                 BT_ERR("Connection could not be established, err: [%d]", status);
46                 result = _bt_convert_oal_status_to_bt_error(status);
47         }
48         return result;
49 }
50
51 int _bt_a2dp_disconnect_remote_source(bluetooth_device_address_t *device_address/*,
52                                                 bt_audio_function_data_t *func_data*/)
53 {
54         oal_status_t status = OAL_STATUS_SUCCESS;
55         int result = BLUETOOTH_ERROR_NONE;
56         BT_INFO("+");
57
58         status = a2dp_sink_disconnect((bt_address_t*)device_address);
59         if (status != OAL_STATUS_SUCCESS) {
60                 BT_ERR("DisConnection err: [%d]", status);
61                 result = _bt_convert_oal_status_to_bt_error(status);
62         }
63         return result;
64 }
65
66 static void __bt_handle_av_source_connected_state(bluetooth_device_address_t *address)
67 {
68         char addr[BT_ADDRESS_STRING_SIZE] = { 0 };
69
70         bt_headset_wait_t *wait_device = NULL;
71         bluetooth_device_address_t wait_device_address;
72         GVariant *param;
73         int result = BLUETOOTH_ERROR_NONE;
74         ret_if(NULL == address);
75         BT_INFO("+");
76
77         _bt_convert_addr_type_to_string(addr, address->addr);
78         BT_INFO("Address of connected device [%s]", addr);
79
80         /* Set VCONF Key for A2DP Connected status */
81         _bt_set_device_values(TRUE, VCONFKEY_BT_DEVICE_A2DP_HEADSET_CONNECTED);
82
83         /* Send A2DP(SRC Role) connected event to Application */
84         param = g_variant_new("(is)", result, addr);
85         _bt_send_event(BT_HEADSET_EVENT, BLUETOOTH_EVENT_AV_SOURCE_CONNECTED, param);
86
87         /* Add data from the connected list */
88         _bt_add_headset_to_list(BT_AUDIO_A2DP_SOURCE, BT_STATE_CONNECTED, addr);
89
90         /* Delete waiting device data if present */
91         wait_device = _bt_get_audio_wait_data();
92         if (wait_device != NULL &&
93                         (g_strcmp0(wait_device->address, addr) == 0))
94                 _bt_rel_wait_data();
95         else {
96                 if (wait_device != NULL) {
97                         BT_INFO("A2DP Profile connected for device [%s] but another device [%s]is waiting to be connnected type [%d]",
98                                         addr, wait_device->address, wait_device->type);
99                         BT_INFO("Trigger connect for the waiting device [%s], audio type [%d]",
100                                         wait_device->address, wait_device->type);
101                         _bt_convert_addr_string_to_type(wait_device_address.addr,
102                                         wait_device->address);
103                         _bt_audio_connect(wait_device->type, &wait_device_address);
104                         /* Now free the wait list */
105                         //_bt_rel_wait_data();
106                 }
107         }
108         BT_INFO("-");
109 }
110
111 static void __bt_handle_av_source_disconnected_state(bluetooth_device_address_t *address)
112 {
113         char addr[BT_ADDRESS_STRING_SIZE] = { 0 };
114         int result = BLUETOOTH_ERROR_NONE;
115         GArray *out_param;
116         bt_headset_wait_t *wait_device;
117         bluetooth_device_address_t device_address;
118         GVariant *param;
119         invocation_info_t *req_info = NULL;
120         ret_if(NULL == address);
121         BT_INFO("+");
122
123         _bt_convert_addr_type_to_string(addr, address->addr);
124
125         /* Set VCONF status for A2DP Disconnection */
126         _bt_set_device_values(FALSE, VCONFKEY_BT_DEVICE_A2DP_HEADSET_CONNECTED);
127
128
129         /* Remove data from the connected list */
130         _bt_remove_headset_from_list(BT_AUDIO_A2DP_SOURCE, addr);
131
132         req_info = _bt_get_request_info_data(BT_AV_SOURCE_DISCONNECT, addr);
133         BT_INFO("Address  of disconnected device[%s]", addr);
134         if (NULL == req_info) {
135                 BT_INFO("AV DisConnect request not found or possibly already replied");
136                 req_info = _bt_get_request_info_data(BT_AV_SOURCE_CONNECT, addr);
137                 if (NULL == req_info) {
138                         /* This means, AV Disconnect request has successfully passed, and real disconnect is completed */
139                         BT_INFO("Neither AV Connect or AV Disconnect request is found..means, AV Profile is disconnected");
140
141                         /* Send A2DP(SRC Role) disconnected event to Application */
142                         result = BLUETOOTH_ERROR_NONE;
143                         param = g_variant_new("(is)", result, addr);
144                         _bt_send_event(BT_HEADSET_EVENT, BLUETOOTH_EVENT_AV_SOURCE_DISCONNECTED, param);
145
146                         /*
147                          * TODO: Below logic is not required for BT_AUDIO_CONNECT/BT_AUDIO_DISCONNECT.
148                          * But if Tizen supports a device with A2DP Sink and HFP both profiles (BT_AUDIO_HFP_SOURCE)
149                          * then below logic will be required for corresponding connect/disconnect function
150                          */
151 #if 0
152                         req_info = _bt_get_request_info_data(BT_AUDIO_CONNECT, addr);
153                         if (req_info == NULL) {
154                                 BT_INFO("Audio Connect All request is also Not found..");
155
156                                 req_info = _bt_get_request_info_data(BT_AUDIO_DISCONNECT, addr);
157                                 if (req_info == NULL)
158                                         BT_INFO("Audio DisConnect All request is also Not found..");
159                                 else
160                                         BT_INFO("Audio DisConnect All request is found..");
161                         } else {
162                                 BT_INFO("Audio Connect All request is found..");
163                                 result = BLUETOOTH_ERROR_INTERNAL;
164                                 data = _bt_get_pending_audio_conn_info(addr);
165                                 if (data) {
166                                         BT_INFO("Pending connect is found, delete it..");
167                                         _bt_cleanup_audio_pending_conn_info_and_reply_pending_req(data, BLUETOOTH_ERROR_NONE);
168                                         req_info = NULL;
169                                 } else {
170                                         BT_INFO("Pending connect is Not found..");
171                                 }
172                         }
173 #endif
174                         goto check_wait_device;
175                 } else {
176                         BT_ERR("AV Connect request has failed.."); /* DBUS return needed */
177                         result = BLUETOOTH_ERROR_INTERNAL;
178
179                         /* Delete waiting device data if present for this device */
180                         wait_device = _bt_get_audio_wait_data();
181                         if (wait_device && strncasecmp(wait_device->address, addr, BT_ADDRESS_STRING_SIZE) == 0) {
182                                 _bt_rel_wait_data();
183                                 goto dbus_return;
184                         }
185
186                         goto check_wait_device;
187                 }
188         } else {
189                 BT_ERR("AV Disconnect request found for [%s], means disconnect request is successful", addr); /* DBUS return needed */
190
191                 /* Send A2DP(SRC Role) disconnected event to Application */
192                 result = BLUETOOTH_ERROR_NONE;
193                 param = g_variant_new("(is)", result, addr);
194                 _bt_send_event(BT_HEADSET_EVENT, BLUETOOTH_EVENT_AV_SOURCE_DISCONNECTED, param);
195
196                 goto check_wait_device;
197         }
198 check_wait_device:
199         wait_device = _bt_get_audio_wait_data();
200
201         if (wait_device == NULL)
202                 goto dbus_return;
203
204         if (((wait_device->type == BT_AUDIO_ALL) &&
205                                 (wait_device->ag_flag == TRUE)) ||
206                         (wait_device->type == BT_AUDIO_A2DP_SOURCE) ||
207                         (wait_device->disconnection_type == BT_AUDIO_A2DP_SOURCE)) {
208                 if (g_strcmp0(wait_device->address, addr) != 0) {
209                         BT_INFO("Trigger connect for the waiting device [%s], audio type [%d]",
210                                         wait_device->address, wait_device->type);
211                         _bt_convert_addr_string_to_type(device_address.addr,
212                                         wait_device->address);
213                         _bt_audio_connect(wait_device->type, &device_address);
214                 }
215         }
216 dbus_return:
217         if (req_info) {
218                 out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
219                 g_array_append_vals(out_param, addr, BT_ADDRESS_STRING_SIZE);
220                 _bt_service_method_return(req_info->context,
221                                 out_param, result);
222                 g_array_free(out_param, TRUE);
223                 _bt_free_info_from_invocation_list(req_info);
224         }
225 }
226
227 static void __bt_reply_av_source_connection_pending_request(bluetooth_device_address_t *address)
228 {
229         BT_DBG("+");
230         char addr[BT_ADDRESS_STRING_SIZE] = { 0 };
231         bluetooth_device_address_t device_address;
232         GArray *out_param;
233         invocation_info_t *req_info;
234         bt_pending_audio_conn_t *data = NULL;
235         int result = BLUETOOTH_ERROR_NONE;
236         memcpy(device_address.addr, address->addr, BLUETOOTH_ADDRESS_LENGTH);
237         _bt_convert_addr_type_to_string(addr, address->addr);
238
239         req_info = _bt_get_request_info_data(BT_AV_SOURCE_CONNECT, addr);
240         if (NULL == req_info) {
241                 BT_INFO("AV source Connect request not found or possibly already replied");
242                 req_info = _bt_get_request_info_data(BT_AUDIO_CONNECT, addr);
243
244                 if (req_info == NULL) {
245                         BT_INFO("AV Connect request & Audio All connect not found or possibly already replied");
246                         return;
247                 } else {
248                         BT_INFO("Audio All connect request found for address [%s]", addr);
249                         /* TODO Delete the search info if present */
250                         data = _bt_get_pending_audio_conn_info(addr);
251                         if (data) {
252                                 BT_INFO("Audio Connect All request present and pending connect is also present, delete info");
253                                 _bt_cleanup_audio_pending_conn_info_and_reply_pending_req(data, BLUETOOTH_ERROR_NONE);
254                         } else {
255                                 BT_ERR("Abnormal: Audio Connect All request present but pending connect absent");
256                         }
257                 }
258         } else {
259                 BT_INFO("AV Connect request found for [%s]", addr);
260         }
261
262         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
263         g_array_append_vals(out_param, addr, BT_ADDRESS_STRING_SIZE);
264         _bt_service_method_return(req_info->context,
265                         out_param, result);
266         g_array_free(out_param, TRUE);
267         _bt_free_info_from_invocation_list(req_info);
268 }
269
270 static void __bt_reply_av_source_disconnection_pending_request(bluetooth_device_address_t *address)
271 {
272         BT_DBG("+");
273         char addr[BT_ADDRESS_STRING_SIZE] = { 0 };
274         bluetooth_device_address_t device_address;
275         GArray *out_param;
276         invocation_info_t *req_info;
277         int result = BLUETOOTH_ERROR_NONE;
278         memcpy(device_address.addr, address->addr, BLUETOOTH_ADDRESS_LENGTH);
279         _bt_convert_addr_type_to_string(addr, address->addr);
280
281         req_info = _bt_get_request_info_data(BT_AV_SOURCE_DISCONNECT, addr);
282         if (NULL == req_info) {
283                 BT_INFO("AV DisConnect request not found or possibly already replied");
284                 req_info = _bt_get_request_info_data(BT_AUDIO_DISCONNECT, addr);
285                 if (req_info == NULL) {
286                         BT_INFO("AUDIO Disconnect All request also not found or possibly already replied");
287                         return;
288                 } else {
289                         BT_INFO("Audio All DisConnect request found..");
290                 }
291         } else {
292                 BT_INFO("AV Source DisConnect request found for [%s]", addr);
293         }
294
295         /* In any of the above cases, do DBUS return */
296         out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
297         g_array_append_vals(out_param, &device_address,
298                         sizeof(bluetooth_device_address_t));
299         _bt_service_method_return(req_info->context,
300                         out_param, result);
301         g_array_free(out_param, TRUE);
302         _bt_free_info_from_invocation_list(req_info);
303 }
304
305 void _bt_a2dp_sink_handle_incoming_authorization(char *address, int service_id)
306 {
307         char connected_address[BT_ADDRESS_STRING_SIZE + 1];
308         gboolean connected;
309         bluetooth_device_address_t device_address;
310         oal_status_t res = OAL_STATUS_SUCCESS;
311         BT_INFO("+");
312
313         _bt_convert_addr_string_to_type(device_address.addr, address);
314
315         switch (service_id) {
316         case A2DP_SRC_SERVICE_ID:
317                 connected = _bt_is_headset_type_connected(BT_AUDIO_A2DP_SOURCE, connected_address);
318                 if (connected) {
319                         BT_INFO("A2DP (Remote Source) is already connected..check which device..");
320                         if (g_strcmp0(connected_address, address) != 0) {
321                                 BT_INFO("Different device is connected[%s] reject current [%s] device authorization request",
322                                                 connected_address, address);
323                                 res = device_reply_auth_request((bt_address_t*)&device_address, service_id, FALSE, FALSE);
324                                 if (res != OAL_STATUS_SUCCESS)
325                                         BT_ERR("authorize_response: %d", res);
326                                 return;
327                         }
328                 } else {
329                         res = device_reply_auth_request((bt_address_t*)&device_address, service_id, TRUE, FALSE);
330                         if (res != OAL_STATUS_SUCCESS)
331                                 BT_ERR("authorize_response: %d", res);
332                 }
333                 break;
334         default:
335                 BT_ERR("Invalid Audio Service ID..");
336         }
337
338         BT_INFO("-");
339 }
340
341 void _bt_a2dp_sink_event_handler(int oal_event, gpointer event_data)
342 {
343         BT_INFO("+");
344         bluetooth_device_address_t * bd_addr = (bluetooth_device_address_t*)event_data;
345
346         switch (oal_event) {
347         case OAL_EVENT_A2DP_SINK_CONNECTING:
348                 BT_INFO("A2DP Sink Profile connecting..");
349                 __bt_reply_av_source_connection_pending_request(bd_addr);
350                 break;
351         case OAL_EVENT_A2DP_SINK_DISCONNECTING:
352                 BT_INFO("A2DP Sink Profile disconnecting..");
353                 __bt_reply_av_source_disconnection_pending_request(bd_addr);
354                 break;
355         case OAL_EVENT_A2DP_SINK_CONNECTED:
356                 BT_INFO("A2DP Sink Profile connected..");
357                 __bt_handle_av_source_connected_state(bd_addr);
358                 BT_PERMANENT_LOG("Connected A2DP sink");
359                 break;
360         case OAL_EVENT_A2DP_SINK_DISCONNECTED:
361                 BT_INFO("A2DP Sink Profile dissconnected..");
362                 __bt_handle_av_source_disconnected_state(bd_addr);
363                 BT_PERMANENT_LOG("Disconnected A2DP sink");
364                 break;
365         default:
366                 BT_INFO("Invalid Event = %d", oal_event);
367                 break;
368         }
369         BT_INFO("-");
370 }