2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include <mime_type.h>
26 #include "bluetooth-api.h"
27 #include "bt-internal-types.h"
29 #include "bt-service-common.h"
30 #include "bt-service-event.h"
31 #include "bt-service-util.h"
32 #include "bt-service-opp-client.h"
33 #include "bt-service-obex-agent.h"
35 static GSList *transfer_list = NULL;
36 bt_sending_info_t *sending_info;
37 static int file_offset = 0;
39 #define DBUS_TIEMOUT 20 * 1000 /* 20 Seconds */
40 static gboolean __bt_sending_release();
41 static void _bt_remove_session();
43 static int __bt_opp_client_start_sending(int request_id, char *address,
44 char **file_name_array, int file_count);
46 static GQuark __bt_opc_error_quark(void)
48 static GQuark quark = 0;
50 quark = g_quark_from_static_string("agent");
55 static void __bt_free_transfer_info(bt_transfer_info_t *info)
60 g_object_unref(info->proxy);
62 if (info->properties_proxy)
63 g_object_unref(info->properties_proxy);
66 g_free(info->transfer_name);
67 g_free(info->file_name);
71 static void __bt_free_sending_info(bt_sending_info_t *info)
75 /* Free the sending variable */
76 __bt_free_transfer_info(info->transfer_info);
78 g_free(info->file_name_array);
80 g_free(info->address);
84 static gboolean __bt_cancel_push_cb(gpointer data)
88 int result = BLUETOOTH_ERROR_CANCEL_BY_USER;
89 GVariant *param = NULL;
90 retv_if(sending_info == NULL, FALSE);
91 sending_info->result = result;
93 param = g_variant_new("(isi)", result,
94 sending_info->address,
95 sending_info->request_id);
96 /* Send the event in only error none case */
97 _bt_send_event(BT_OPP_CLIENT_EVENT,
98 BLUETOOTH_EVENT_OPC_CONNECTED,
100 __bt_free_sending_info(sending_info);
103 _bt_opp_client_event_deinit();
105 BT_DBG("Length of transfer list is %d", g_slist_length(transfer_list));
107 /*Operate remain works*/
108 if (g_slist_length(transfer_list) > 0) {
109 bt_sending_data_t *node = NULL;
111 node = transfer_list->data;
113 BT_ERR("data is NULL");
117 transfer_list = g_slist_remove(transfer_list, node);
119 if (__bt_opp_client_start_sending(node->request_id,
120 node->address, node->file_path,
121 node->file_count) != BLUETOOTH_ERROR_NONE) {
122 BT_ERR("Fail to start sending");
129 gboolean _bt_obex_client_progress(const char *transfer_path, int transferred)
133 int percentage_progress;
135 int result = BLUETOOTH_ERROR_NONE;
136 GVariant *param = NULL;
137 retv_if(sending_info == NULL, TRUE);
138 retv_if(sending_info->transfer_info == NULL, TRUE);
140 if (g_strcmp0(sending_info->transfer_info->transfer_path,
141 transfer_path) != 0) {
142 BT_INFO("Path mismatch, previous transfer failed! Returning");
146 size = sending_info->transfer_info->size;
149 percentage_progress = (int)(((gdouble)transferred /
150 (gdouble)size) * 100);
152 percentage_progress = 0;
154 sending_info->transfer_info->transfer_status = BT_TRANSFER_STATUS_PROGRESS;
155 sending_info->result = result;
157 /* Send the event in only error none case */
158 param = g_variant_new("(istii)", result,
159 sending_info->transfer_info->file_name,
160 sending_info->transfer_info->size,
162 sending_info->request_id);
163 _bt_send_event(BT_OPP_CLIENT_EVENT,
164 BLUETOOTH_EVENT_OPC_TRANSFER_PROGRESS,
171 gboolean _bt_obex_client_completed(const char *transfer_path, gboolean success)
175 int result = BLUETOOTH_ERROR_NONE;
176 GVariant *param = NULL;
177 retv_if(sending_info == NULL, TRUE);
178 retv_if(sending_info->transfer_info == NULL, TRUE);
180 if (g_strcmp0(sending_info->transfer_info->transfer_path,
181 transfer_path) != 0) {
182 BT_INFO("Path mismatch, previous transfer failed! Returning");
186 result = (success == TRUE) ? BLUETOOTH_ERROR_NONE : BLUETOOTH_ERROR_CANCEL;
188 sending_info->transfer_info->transfer_status = BT_TRANSFER_STATUS_COMPLETED;
189 sending_info->result = result;
191 if (!success) { /*In case of remote device reject, we need to send BLUETOOTH_EVENT_OPC_DISCONNECTED */
192 BT_DBG("completed with error");
193 if (!sending_info->is_canceled) {
194 param = g_variant_new("(isti)", result,
195 sending_info->transfer_info->file_name,
196 sending_info->transfer_info->size,
197 sending_info->request_id);
198 _bt_send_event(BT_OPP_CLIENT_EVENT,
199 BLUETOOTH_EVENT_OPC_TRANSFER_COMPLETE,
201 __bt_free_transfer_info(sending_info->transfer_info);
202 sending_info->transfer_info = NULL;
203 /* Reset the file offset as we will cancelled remaining files also */
206 param = g_variant_new("(isi)", sending_info->result,
207 sending_info->address,
208 sending_info->request_id);
209 _bt_send_event(BT_OPP_CLIENT_EVENT,
210 BLUETOOTH_EVENT_OPC_DISCONNECTED,
212 __bt_sending_release();
213 /* Sending info should not freed after sending_release it's
214 * already freed in that API and if any pending request is
215 * present then it recreate sending_info again.
216 * And if we free it here then CreateSession method call will
217 * made but RemoveSession method call will not done.
220 BT_DBG("complete success");
221 /* Send the event in only error none case */
222 param = g_variant_new("(isti)", result,
223 sending_info->transfer_info->file_name,
224 sending_info->transfer_info->size,
225 sending_info->request_id);
226 _bt_send_event(BT_OPP_CLIENT_EVENT,
227 BLUETOOTH_EVENT_OPC_TRANSFER_COMPLETE,
229 __bt_free_transfer_info(sending_info->transfer_info);
230 sending_info->transfer_info = NULL;
238 gboolean _bt_obex_client_started(const char *transfer_path)
242 int result = BLUETOOTH_ERROR_NONE;
243 GError *error = NULL;
244 GVariant *param = NULL;
245 GDBusConnection *g_conn;
246 GDBusProxy *properties_proxy;
247 GDBusProxy *transfer_proxy;
249 if (sending_info == NULL || sending_info->is_canceled == TRUE) {
250 result = BLUETOOTH_ERROR_CANCEL_BY_USER;
254 /* Get the session bus. */
255 g_conn = _bt_get_session_gconn();
256 retv_if(g_conn == NULL, FALSE);
257 properties_proxy = g_dbus_proxy_new_sync(g_conn, G_DBUS_PROXY_FLAGS_NONE,
258 NULL,BT_OBEXD_DBUS_NAME,
259 transfer_path, BT_PROPERTIES_INTERFACE,
262 retv_if(properties_proxy == NULL, FALSE);
264 sending_info->transfer_info->properties_proxy = properties_proxy;
266 transfer_proxy = g_dbus_proxy_new_sync(g_conn, G_DBUS_PROXY_FLAGS_NONE,
267 NULL, BT_OBEXD_DBUS_NAME,
268 transfer_path, BT_OBEX_TRANSFER_INTERFACE,
271 retv_if(transfer_proxy == NULL, FALSE);
273 sending_info->transfer_info->proxy = transfer_proxy;
275 sending_info->transfer_info->transfer_status = BT_TRANSFER_STATUS_STARTED;
276 sending_info->result = result;
277 param = g_variant_new("(isti)", result,
278 sending_info->transfer_info->file_name,
279 sending_info->transfer_info->size,
280 sending_info->request_id);
281 _bt_send_event(BT_OPP_CLIENT_EVENT,
282 BLUETOOTH_EVENT_OPC_TRANSFER_STARTED,
288 error = g_error_new(__bt_opc_error_quark(), BT_OBEX_AGENT_ERROR_CANCEL,
297 static void __bt_free_sending_data(gpointer data)
300 bt_sending_data_t *info = data;
302 ret_if(info == NULL);
304 for (i = 0; i < info->file_count; i++) {
305 g_free(info->file_path[i]);
308 _bt_delete_request_id(info->request_id);
310 g_free(info->file_path);
311 g_free(info->address);
315 static void __bt_sending_release_cb(GDBusProxy *proxy,
316 GAsyncResult *res, gpointer user_data)
319 ret_if(sending_info == NULL);
321 GError *error = NULL;
322 int result = BLUETOOTH_ERROR_NONE;
323 GVariant *param = NULL;
324 g_dbus_proxy_call_finish(proxy, res, &error);
326 g_object_unref(proxy);
329 BT_ERR("%s", error->message);
332 result = BLUETOOTH_ERROR_INTERNAL;
335 BT_DBG("Session Removed");
338 if (sending_info->result != BLUETOOTH_ERROR_CANCEL_BY_USER)
339 sending_info->result = result;
341 param = g_variant_new("(isi)", sending_info->result,
342 sending_info->address,
343 sending_info->request_id);
344 /* Send the event in only error none case */
345 _bt_send_event(BT_OPP_CLIENT_EVENT,
346 BLUETOOTH_EVENT_OPC_DISCONNECTED,
349 __bt_free_sending_info(sending_info);
352 _bt_opp_client_event_deinit();
354 /* Operate remain works */
355 if (g_slist_length(transfer_list) > 0) {
356 bt_sending_data_t *data = NULL;
358 data = transfer_list->data;
362 transfer_list = g_slist_remove(transfer_list, data);
364 BT_DBG("calling __bt_opp_client_start_sending");
366 if (__bt_opp_client_start_sending(data->request_id,
367 data->address, data->file_path,
368 data->file_count) != BLUETOOTH_ERROR_NONE) {
375 g_slist_free_full(transfer_list,
376 (GDestroyNotify)__bt_free_sending_data);
377 transfer_list = NULL;
384 static void _bt_remove_session()
386 GDBusConnection *g_conn;
387 GDBusProxy *session_proxy;
390 g_conn = _bt_get_session_gconn();
391 ret_if(g_conn == NULL);
392 ret_if(sending_info->session_path == NULL);
394 session_proxy = g_dbus_proxy_new_sync(g_conn, G_DBUS_PROXY_FLAGS_NONE,
395 NULL, BT_OBEXD_DBUS_NAME,
397 BT_OBEX_CLIENT_INTERFACE,
400 ret_if(session_proxy == NULL);
402 g_dbus_proxy_call(session_proxy, "RemoveSession",
403 g_variant_new("(o)", sending_info->session_path),
404 G_DBUS_CALL_FLAGS_NONE,
406 (GAsyncReadyCallback)__bt_sending_release_cb,
411 static gboolean __bt_sending_release()
415 retv_if(sending_info == NULL, FALSE);
417 _bt_remove_session();
422 void _bt_opc_disconnected(const char *session_path)
425 GVariant *param = NULL;
426 ret_if(sending_info == NULL);
428 if (g_strcmp0(sending_info->session_path,
429 session_path) != 0) {
430 BT_INFO("Path mismatch, previous transfer failed! Returning");
434 if (sending_info->transfer_info) {
435 if (sending_info->transfer_info->transfer_status == BT_TRANSFER_STATUS_PROGRESS ||
436 sending_info->transfer_info->transfer_status == BT_TRANSFER_STATUS_STARTED) {
437 BT_INFO("Abnormal termination");
438 param = g_variant_new("(isti)", sending_info->result,
439 sending_info->transfer_info->file_name,
440 sending_info->transfer_info->size,
441 sending_info->request_id);
442 _bt_send_event(BT_OPP_CLIENT_EVENT,
443 BLUETOOTH_EVENT_OPC_TRANSFER_COMPLETE,
445 __bt_free_transfer_info(sending_info->transfer_info);
448 param = g_variant_new("(isi)", sending_info->result,
449 sending_info->address,
450 sending_info->request_id);
451 _bt_send_event(BT_OPP_CLIENT_EVENT,
452 BLUETOOTH_EVENT_OPC_DISCONNECTED,
455 __bt_free_sending_info(sending_info);
461 static void __bt_send_file_cb(GDBusProxy *proxy,
462 GAsyncResult *res, gpointer user_data)
465 GVariant *value = NULL;
466 GError *error = NULL;
467 char *session_path = NULL;
468 const char *transfer_name = NULL;
469 const char *file_name = NULL;
471 GVariantIter *iter = NULL;
472 value = g_dbus_proxy_call_finish(proxy, res, &error);
474 BT_ERR("%s", error->message);
477 g_object_unref(proxy);
481 g_object_unref(proxy);
484 g_variant_get(value, "(oa{sv})", &session_path, &iter);
485 g_variant_unref(value);
488 __bt_free_transfer_info(sending_info->transfer_info);
490 sending_info->transfer_info = g_malloc0(sizeof(bt_transfer_info_t));
496 while (g_variant_iter_loop(iter, "{sv}", &key, &val)) {
497 if (g_strcmp0(key, "Name") == 0) {
498 transfer_name = g_variant_dup_string(val,&len);
499 } else if (g_strcmp0(key, "Filename") == 0) {
500 file_name = g_variant_dup_string(val, &len);
501 } else if (g_strcmp0(key, "Size") == 0) {
502 size = g_variant_get_uint64(val);
505 g_variant_iter_free(iter);
508 sending_info->transfer_info->transfer_name = g_strdup(transfer_name);
509 sending_info->transfer_info->file_name = g_strdup(file_name);
510 sending_info->transfer_info->size = size;
511 sending_info->transfer_info->transfer_path = session_path;
512 sending_info->transfer_info->transfer_status = BT_TRANSFER_STATUS_QUEUED;
513 sending_info->result = BLUETOOTH_ERROR_NONE;
518 void _bt_sending_files(void)
523 GDBusConnection *g_conn;
524 GDBusProxy *client_proxy;
525 char *mimetype = NULL;
528 if (sending_info == NULL)
530 if (file_offset < sending_info->file_count){
531 /* Get the session bus. */
532 g_conn = _bt_get_session_gconn();
533 ret_if(g_conn == NULL);
535 client_proxy = g_dbus_proxy_new_sync(g_conn, G_DBUS_PROXY_FLAGS_NONE,
536 NULL, BT_OBEXD_DBUS_NAME,
537 sending_info->session_path,
538 BT_OBEX_OBJECT_PUSH_INTERFACE,
540 ret_if(client_proxy == NULL);
542 BT_DBG("Calling SendFile");
543 ext = strrchr(sending_info->file_name_array[file_offset], '.');
545 if (ext != NULL && (!strcmp(ext, ".imy")))
546 mimetype = g_strdup("audio/imelody");
547 g_dbus_proxy_call(client_proxy, "SendFile",
548 g_variant_new("(ss)", sending_info->file_name_array[file_offset],
550 G_DBUS_CALL_FLAGS_NONE,
552 (GAsyncReadyCallback)__bt_send_file_cb,
555 BT_ERR("Calling SendFile failed: [%s]\n", err->message);
564 __bt_sending_release();
570 static void __bt_create_session_cb(GDBusProxy *proxy,
571 GAsyncResult *res, gpointer user_data)
575 GError *error = NULL;
577 int result = BLUETOOTH_ERROR_NONE;
578 char *session_path = NULL;
579 GVariant *param = NULL;
581 value = g_dbus_proxy_call_finish(proxy, res, &error);
583 g_object_unref(proxy);
586 g_variant_get(value, "(o)", &session_path);
587 g_variant_unref(value);
591 BT_ERR("%s", error->message);
592 g_clear_error(&error);
594 result = BLUETOOTH_ERROR_INTERNAL;
596 BT_DBG("Session created");
597 if(sending_info != NULL)
598 sending_info->session_path = g_strdup(session_path);
600 g_free(session_path);
601 ret_if(sending_info == NULL);
603 sending_info->result = result;
604 param = g_variant_new("(isi)", result,
605 sending_info->address,
606 sending_info->request_id);
607 /* Send the event in only error none case */
608 _bt_send_event(BT_OPP_CLIENT_EVENT,
609 BLUETOOTH_EVENT_OPC_CONNECTED,
612 if (result != BLUETOOTH_ERROR_NONE) {
613 BT_ERR("Calling __bt_sending_release");
614 __bt_sending_release();
616 __bt_free_sending_info(sending_info);
619 BT_DBG("Calling sending_files");
626 static int __bt_opp_client_start_sending(int request_id, char *address,
627 char **file_name_array, int file_count)
629 GVariantBuilder *builder;
631 GDBusConnection *g_conn;
632 GDBusProxy *client_proxy;
633 GError *error = NULL;
636 BT_CHECK_PARAMETER(address, return);
637 BT_CHECK_PARAMETER(file_name_array, return);
639 /* Get the session bus. */
640 g_conn = _bt_get_session_gconn();
641 retv_if(g_conn == NULL, BLUETOOTH_ERROR_INTERNAL);
643 client_proxy = g_dbus_proxy_new_sync(g_conn, G_DBUS_PROXY_FLAGS_NONE,
644 NULL, BT_OBEX_SERVICE_NAME,
646 BT_OBEX_CLIENT_INTERFACE,
650 BT_ERR("Unable to create client proxy: %s", error->message);
651 g_clear_error(&error);
654 retv_if(client_proxy == NULL, BLUETOOTH_ERROR_INTERNAL);
656 builder = g_variant_builder_new(
657 G_VARIANT_TYPE("a{sv}"));
659 g_variant_builder_add(builder, "{sv}", "Target",
660 g_variant_new_string("OPP"));
662 __bt_free_sending_info(sending_info);
664 sending_info = g_malloc0(sizeof(bt_sending_info_t));
665 sending_info->address = g_strdup(address);
666 sending_info->request_id = request_id;
668 sending_info->file_count = file_count;
669 sending_info->file_offset = 0;
670 sending_info->file_name_array = g_new0(char *, file_count + 1);
672 for (i = 0; i < file_count; i++) {
673 sending_info->file_name_array[i] = g_strdup(file_name_array[i]);
674 BT_DBG("file[%d]: %s", i, sending_info->file_name_array[i]);
677 _bt_opp_client_event_deinit();
678 _bt_opp_client_event_init();
679 //_bt_obex_client_started(agent_path);
681 BT_DBG("Going to call CreateSession");
683 g_dbus_proxy_call(client_proxy, "CreateSession",
684 g_variant_new("(sa{sv})", address, builder),
685 G_DBUS_CALL_FLAGS_NONE,
687 (GAsyncReadyCallback)__bt_create_session_cb,
689 g_variant_builder_unref(builder);
693 return BLUETOOTH_ERROR_NONE;
696 int _bt_opp_client_push_files(int request_id, GDBusMethodInvocation *context,
697 bluetooth_device_address_t *remote_address,
698 char **file_path, int file_count)
701 char address[BT_ADDRESS_STRING_SIZE] = { 0 };
702 bt_sending_data_t *data;
704 GVariant *out_param1 = NULL;
706 int result = BLUETOOTH_ERROR_NONE;
709 BT_CHECK_PARAMETER(remote_address, return);
710 BT_CHECK_PARAMETER(file_path, return);
712 /* Implement the queue */
713 _bt_convert_addr_type_to_string(address, remote_address->addr);
715 if (sending_info == NULL) {
716 result = __bt_opp_client_start_sending(request_id,
717 address, file_path, file_count);
718 if (result != BLUETOOTH_ERROR_NONE)
721 /* Insert data in the queue */
722 data = g_malloc0(sizeof(bt_sending_data_t));
723 /* Fix : NULL_RETURNS */
725 return BLUETOOTH_ERROR_MEMORY_ALLOCATION;
727 data->file_path = g_new0(char *, file_count + 1);
728 data->address = g_strdup(address);
729 data->file_count = file_count;
730 data->request_id = request_id;
732 for (i = 0; i < file_count; i++) {
733 data->file_path[i] = g_strdup(file_path[i]);
734 DBG_SECURE("file[%d]: %s", i, data->file_path[i]);
737 transfer_list = g_slist_append(transfer_list, data);
740 out_param1 = g_variant_new_from_data((const GVariantType *)"ay",
741 &request_id, sizeof(int),
745 g_dbus_method_invocation_return_value(context,
746 g_variant_new("(iv)", result, out_param1));
753 int _bt_opp_client_cancel_push(void)
758 int result = BLUETOOTH_ERROR_CANCEL_BY_USER;
759 GVariant *param = NULL;
760 retv_if(sending_info == NULL, BLUETOOTH_ERROR_NOT_IN_OPERATION);
762 sending_info->is_canceled = TRUE;
763 sending_info->result = result;
765 if (sending_info->transfer_info) {
767 g_dbus_proxy_call_sync(sending_info->transfer_info->proxy,
769 G_DBUS_CALL_FLAGS_NONE, -1,
771 param = g_variant_new("(isti)", result,
772 sending_info->transfer_info->file_name,
773 sending_info->transfer_info->size,
774 sending_info->request_id);
775 _bt_send_event(BT_OPP_CLIENT_EVENT,
776 BLUETOOTH_EVENT_OPC_TRANSFER_COMPLETE,
779 if (result == BLUETOOTH_ERROR_CANCEL_BY_USER) {
780 BT_ERR("result is not BLUETOOTH_ERROR_NONE");
781 __bt_sending_release();
786 g_idle_add(__bt_cancel_push_cb, NULL);
791 return BLUETOOTH_ERROR_NONE;
794 int _bt_opp_client_cancel_all_transfers(void)
798 g_slist_free_full(transfer_list,
799 (GDestroyNotify)__bt_free_sending_data);
801 transfer_list = NULL;
804 _bt_opp_client_cancel_push();
806 return BLUETOOTH_ERROR_NONE;
809 int _bt_opp_client_is_sending(gboolean *sending)
811 BT_CHECK_PARAMETER(sending, return);
813 *sending = sending_info ? TRUE : FALSE;
815 return BLUETOOTH_ERROR_NONE;
818 void _bt_opp_client_check_pending_transfer(const char *address)
822 int result = BLUETOOTH_ERROR_CANCEL;
823 GVariant *param = NULL;
824 ret_if(sending_info == NULL);
825 ret_if(sending_info->transfer_info == NULL);
827 if (g_strcmp0(sending_info->address, address) == 0) {
828 BT_INFO("Address Match.Cancel current transfer");
829 sending_info->transfer_info->transfer_status = BT_TRANSFER_STATUS_COMPLETED;
830 sending_info->result = result;
832 if (!sending_info->is_canceled) {
833 param = g_variant_new("(isti)", result,
834 sending_info->transfer_info->file_name,
835 sending_info->transfer_info->size,
836 sending_info->request_id);
837 _bt_send_event(BT_OPP_CLIENT_EVENT,
838 BLUETOOTH_EVENT_OPC_TRANSFER_COMPLETE,
840 __bt_free_transfer_info(sending_info->transfer_info);
841 sending_info->transfer_info = NULL;
842 /* Reset the file offset as we will cancelled remaining files also */
845 param = g_variant_new("(isi)", sending_info->result,
846 sending_info->address,
847 sending_info->request_id);
848 _bt_send_event(BT_OPP_CLIENT_EVENT,
849 BLUETOOTH_EVENT_OPC_DISCONNECTED,
852 __bt_sending_release();