4 * Copyright (c) 2013-2014 Intel Corporation.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 #include <sys/types.h>
26 #include <dbus/dbus.h>
27 #include <gio/gunixfdlist.h>
30 #include "bluetooth.h"
33 #include "bluetooth-service.h"
36 #define OBEX_LIB_SERVICE "org.obex.lib"
37 #define AGENT_OBJECT_PATH "/org/obex/lib"
39 #define OBEX_ERROR_INTERFACE "org.bluez.obex.Error"
41 #define ADDRESS_LEN 20
42 static char pushing_address[ADDRESS_LEN];
45 BT_OPP_TRANSFER_UNKNOWN = 0x0,
46 BT_OPP_TRANSFER_QUEUED,
47 BT_OPP_TRANSFER_ACTIVE,
48 BT_OPP_TRANSFER_COMPLETED,
49 BT_OPP_TRANSFER_CANCELED,
50 BT_OPP_TRANSFER_ERROR,
51 } bt_opp_transfer_state_e;
53 typedef void (*bt_opp_server_push_file_requested_cb)(
54 const char *remote_address,
59 typedef void (*bt_opp_transfer_state_cb)(
61 bt_opp_transfer_state_e state,
64 unsigned char percent,
68 BT_OPP_PUSH_ACCETPED = 0,
73 BT_OPP_PUSH_NO_SERVICE
76 typedef void (*bt_opp_client_push_responded_new_cb)(
77 const char *remote_address,
84 char *pending_transfer_path;
85 bt_opp_server_push_file_requested_cb requested_cb;
87 int pending_transfer_id;
88 GDBusMethodInvocation *pending_invocation;
91 struct opp_transfer_state_cb_node {
92 bt_opp_transfer_state_cb cb;
96 static gboolean is_client_register;
97 static gboolean is_server_register;
98 static void *opp_client_data;
100 static guint bluetooth_ext_agent_id;
102 static struct opp_transfer_state_cb_node *opp_transfer_state_node;
104 static void bt_opp_server_transfer_state_cb(bt_opp_transfer_state_e state,
105 const char *name, uint64_t size,
106 unsigned char percent, void *user_data);
108 static void bt_opp_client_transfer_state_cb(unsigned int id,
109 bt_opp_transfer_state_e state, const char *address,
110 const char *name, uint64_t size,
111 unsigned char percent, void *user_data);
113 static int bt_opp_server_reject_request(void);
115 static int bt_device_get_privileges(const char *remote_address)
117 int user_privilieges;
119 DBG("address = %s", remote_address);
121 user_privilieges = comms_bluetooth_get_user_privileges_sync(
124 return user_privilieges;
127 static GDBusNodeInfo *introspection_data;
129 static const gchar introspection_xml[] =
131 " <interface name='org.bluez.obex.Agent1'>"
132 " <method name='Release'>"
134 " <method name='AuthorizePush'>"
135 " <arg type='s' name='address' direction='in'/>"
136 " <arg type='s' name='name' direction='in'/>"
137 " <arg type='s' name='path' direction='in'/>"
138 " <arg type='t' name='size' direction='in'/>"
139 " <arg type='i' name='transfer_id' direction='in'/>"
140 " <arg type='s' direction='out'/>"
142 " <method name='Cancel'>"
147 static void handle_cancel(GDBusMethodInvocation *invocation)
151 g_dbus_method_invocation_return_value(invocation, NULL);
154 static void handle_release(GDBusMethodInvocation *invocation)
158 g_dbus_method_invocation_return_value(invocation, NULL);
161 static void bt_opp_manager_service_watch(
162 gchar *address, gchar *name,
163 guint64 size, guint id,
164 guint state, double percent,
167 DBG("transfer_id = %d, state = %d", id, state);
170 bt_opp_server_transfer_state_cb(state, name, size,
171 percent, opp_server.user_data);
173 bt_opp_client_transfer_state_cb(id, state,
174 address, name, size, percent, opp_client_data);
177 static void handle_method_call(GDBusConnection *connection,
179 const gchar *object_path,
180 const gchar *interface_name,
181 const gchar *method_name,
182 GVariant *parameters,
183 GDBusMethodInvocation *invocation,
187 DBG("%s", method_name);
189 if (g_strcmp0(method_name, "Release") == 0) {
190 handle_release(invocation);
192 } else if (g_strcmp0(method_name, "AuthorizePush") == 0) {
193 gchar *address, *name, *transfer_path;
194 gchar *device_name = NULL;
195 bluez_adapter_t *adapter;
196 bluez_device_t *device;
201 opp_server.pending_invocation = invocation;
203 g_variant_get(parameters, "(sssti)", &address,
204 &name, &transfer_path, &size, &transfer_id);
206 privilieges = bt_device_get_privileges(address);
207 if (privilieges == 0) {
208 DBG("user not privilieges to pair and use");
209 /*todo: This point will check if Cynara allow user
210 use the remote device
211 if ok, return BT_SUCCESS.
213 bt_opp_server_reject_request();
217 adapter = bluez_adapter_get_adapter(DEFAULT_ADAPTER_NAME);
218 device = bluez_adapter_get_device_by_address(adapter,
221 device_name = bluez_device_get_property_alias(device);
223 opp_server.pending_name = g_strdup(name);
224 opp_server.pending_transfer_path = g_strdup(transfer_path);
225 opp_server.pending_transfer_id = transfer_id;
227 if (opp_server.requested_cb)
228 opp_server.requested_cb((const char *) device_name,
230 size, opp_server.user_data);
235 } else if (g_strcmp0(method_name, "Cancel") == 0) {
236 handle_cancel(invocation);
242 static const GDBusInterfaceVTable interface_handle = {
248 static GDBusConnection *conn;
249 static guint bluetooth_opp_agent_id;
251 static GDBusConnection *get_system_dbus_connect(void)
253 GError *error = NULL;
258 conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
260 DBG("%s", error->message);
270 static void release_dbus_connection(void)
272 g_object_unref(conn);
276 static void release_name_on_dbus(const char *name)
279 guint32 request_name_reply;
280 GError *error = NULL;
282 if (bluetooth_opp_agent_id)
285 if (bluetooth_ext_agent_id)
288 ret = g_dbus_connection_call_sync(conn, "org.freedesktop.DBus",
289 "/org/freedesktop/DBus", "org.freedesktop.DBus",
290 "ReleaseName", g_variant_new("(s)", name),
291 G_VARIANT_TYPE("(u)"), G_DBUS_CALL_FLAGS_NONE,
294 WARN("%s", error->message);
298 g_variant_get(ret, "(u)", &request_name_reply);
299 g_variant_unref(ret);
301 if (request_name_reply != 1) {
302 WARN("Unexpected reply");
306 release_dbus_connection();
311 static int request_name_on_dbus(const char *name)
313 GDBusConnection *connection;
315 guint32 request_name_reply;
316 GError *error = NULL;
318 if (bluetooth_opp_agent_id)
321 connection = get_system_dbus_connect();
322 if (connection == NULL)
325 ret = g_dbus_connection_call_sync(connection, "org.freedesktop.DBus",
326 "/org/freedesktop/DBus", "org.freedesktop.DBus",
327 "RequestName", g_variant_new("(su)", name,
328 G_BUS_NAME_OWNER_FLAGS_NONE),
329 G_VARIANT_TYPE("(u)"), G_DBUS_CALL_FLAGS_NONE,
332 WARN("%s", error->message);
338 g_variant_get(ret, "(u)", &request_name_reply);
339 g_variant_unref(ret);
341 /* RequestName will return the uint32 value:
342 * 1: DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
343 * 2: BUS_REQUEST_NAME_REPLY_IN_QUEUE
344 * 3: DBUS_REQUEST_NAME_REPLY_EXISTS
345 * 4: DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER
348 if (request_name_reply == DBUS_REQUEST_NAME_REPLY_IN_QUEUE
349 || request_name_reply == DBUS_REQUEST_NAME_REPLY_EXISTS
350 || request_name_reply ==
351 DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER) {
352 bluetooth_ext_agent_id = 1;
356 if (request_name_reply != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
359 release_name_on_dbus(name);
364 if (bluetooth_ext_agent_id > 0)
365 bluetooth_ext_agent_id = 0;
369 g_object_unref(connection);
374 static void destroy_opp_object(void)
376 DBG("bluetooth_opp_agent_id = %d", bluetooth_opp_agent_id);
378 if (is_client_register || is_server_register)
381 if (bluetooth_opp_agent_id > 0) {
382 g_dbus_connection_unregister_object(conn,
383 bluetooth_opp_agent_id);
384 bluetooth_opp_agent_id = 0;
385 release_name_on_dbus(OBEX_LIB_SERVICE);
391 static void destroy_opp_agent(void)
393 DBG("bluetooth_opp_agent_id = %d", bluetooth_opp_agent_id);
395 destroy_opp_object();
397 comms_bluetooth_unregister_opp_agent(
398 AGENT_OBJECT_PATH, NULL, NULL);
403 static int register_opp_object(void)
409 if (bluetooth_opp_agent_id)
413 g_dbus_node_info_new_for_xml(introspection_xml, NULL);
415 ret = request_name_on_dbus(OBEX_LIB_SERVICE);
417 return BT_ERROR_OPERATION_FAILED;
419 DBG("%s requested success", OBEX_LIB_SERVICE);
421 bluetooth_opp_agent_id = g_dbus_connection_register_object(conn,
423 introspection_data-> interfaces[0],
424 &interface_handle, NULL, NULL, NULL);
426 DBG("bluetooth_opp_agent_id = %d", bluetooth_opp_agent_id);
428 if (bluetooth_opp_agent_id == 0)
429 return BT_ERROR_OPERATION_FAILED;
434 static int register_opp_agent(void)
440 ret = register_opp_object();
442 if (ret == BT_SUCCESS) {
443 if (is_server_register)
446 return BT_ERROR_OPERATION_FAILED;
448 ret = comms_bluetooth_register_opp_agent_sync(
449 AGENT_OBJECT_PATH, NULL);
451 DBG("ret = %d", ret);
453 if (ret != BT_SUCCESS) {
454 is_server_register = FALSE;
456 return BT_ERROR_OPERATION_FAILED;
462 static int bt_opp_register_server(const char *dir,
463 bt_opp_server_push_file_requested_cb push_requested_cb,
468 if (dir == NULL || push_requested_cb == NULL)
469 return BT_ERROR_INVALID_PARAMETER;
471 if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
472 ERROR("%s is not valid directory", dir);
473 return BT_ERROR_INVALID_PARAMETER;
476 ret = register_opp_agent();
477 if (ret != BT_SUCCESS)
480 is_server_register = TRUE;
482 opp_manager_set_service_watch(bt_opp_manager_service_watch,
485 /* TODO: Do we need to check the dir privilege? */
487 opp_server.root_folder = g_strdup(dir);
488 opp_server.requested_cb = push_requested_cb;
489 opp_server.user_data = user_data;
494 static int bt_opp_unregister_server(void)
496 /* TODO: unregister agent */
497 g_free(opp_server.root_folder);
498 opp_server.root_folder = NULL;
500 opp_server.requested_cb = NULL;
501 opp_server.user_data = NULL;
503 is_server_register = FALSE;
506 if (is_client_register)
509 opp_manager_remove_service_watch();
514 int bt_opp_set_transfers_state_cb(bt_opp_transfer_state_cb cb, void *user_data)
516 struct opp_transfer_state_cb_node *state_node;
521 return BT_ERROR_INVALID_PARAMETER;
523 if (opp_transfer_state_node) {
524 DBG("transfer state callback already set.");
525 return BT_ERROR_ALREADY_DONE;
528 state_node = g_new0(struct opp_transfer_state_cb_node, 1);
529 if (state_node == NULL) {
531 return BT_ERROR_OUT_OF_MEMORY;
535 state_node->user_data = user_data;
537 opp_transfer_state_node = state_node;
542 void bt_opp_clear_transfers_state_cb(void)
546 if (!opp_transfer_state_node)
549 g_free(opp_transfer_state_node);
550 opp_transfer_state_node = NULL;
553 int bt_opp_server_accept_request(const char *name, void *user_data,
556 GDBusMethodInvocation *invocation;
559 invocation = opp_server.pending_invocation;
561 if (invocation == NULL) {
562 ERROR("Can't find invocation");
563 return BT_ERROR_OPERATION_FAILED;
566 n = (name != NULL) ? (char *) name : opp_server.pending_name;
568 if (opp_server.root_folder) {
569 file_name = g_build_filename(opp_server.root_folder, n, NULL);
570 g_dbus_method_invocation_return_value(invocation,
571 g_variant_new("(s)", file_name));
574 g_dbus_method_invocation_return_value(invocation,
575 g_variant_new("(s)", n));
577 opp_server.pending_invocation = NULL;
578 opp_server.user_data = user_data;
580 comms_bluetooth_opp_add_notify(opp_server.pending_transfer_path,
583 *transfer_id = opp_server.pending_transfer_id;
585 g_free(opp_server.pending_transfer_path);
586 opp_server.pending_transfer_path = NULL;
587 g_free(opp_server.pending_name);
588 opp_server.pending_name = NULL;
593 int bt_opp_server_reject_request(void)
595 if (opp_server.pending_invocation) {
596 g_dbus_method_invocation_return_dbus_error(
597 opp_server.pending_invocation,
598 OBEX_ERROR_INTERFACE ".Rejected",
601 opp_server.pending_invocation = NULL;
604 opp_server.user_data = NULL;
605 g_free(opp_server.pending_transfer_path);
606 opp_server.pending_transfer_path = NULL;
607 g_free(opp_server.pending_name);
608 opp_server.pending_name = NULL;
613 int bt_opp_transfer_cancel(int transfer_id)
615 comms_bluetooth_opp_cancel_transfer(transfer_id, NULL, NULL);
619 int bt_opp_server_set_destination(const char *dir)
622 return BT_ERROR_INVALID_PARAMETER;
624 if (opp_server.root_folder != NULL)
625 g_free(opp_server.root_folder);
627 opp_server.root_folder = g_strdup(dir);
632 struct opp_push_data{
634 bt_opp_client_push_responded_cb responded_cb;
635 void *responded_data;
636 bt_opp_transfer_state_cb transfer_state_cb;
640 int bt_opp_client_push_file(const char *remote_address)
642 if (remote_address == NULL)
643 return BT_ERROR_INVALID_PARAMETER;
645 return comms_bluetooth_opp_send_file(remote_address,
646 AGENT_OBJECT_PATH, NULL, NULL);
649 /* Deprecate OPP APIs.
650 * Always implement using NEW OPP APIs*/
651 struct opp_server_push_cb_node {
652 bt_opp_server_push_requested_cb callback;
656 struct opp_server_connection_requested_cb {
657 bt_opp_server_connection_requested_cb callback;
661 struct opp_server_push_cb_node *opp_server_push_node;
662 struct opp_server_connection_requested_cb *opp_server_conn_req_node;
663 static bt_opp_server_transfer_progress_cb bt_transfer_progress_cb;
664 static bt_opp_server_transfer_finished_cb bt_transfer_finished_cb;
665 static bt_opp_client_push_progress_cb bt_progress_cb;
666 static bt_opp_client_push_responded_cb bt_push_responded_cb;
667 static bt_opp_client_push_finished_cb bt_finished_cb;
669 void server_push_requested_cb(const char *remote_address, const char *name,
670 uint64_t size, void *user_data)
672 if (opp_server_push_node)
673 opp_server_push_node->callback(name, size,
674 opp_server_push_node->user_data);
677 void server_connect_requested_cb(const char *remote_address, const char *name,
678 uint64_t size, void *user_data)
680 if (opp_server_conn_req_node)
681 opp_server_conn_req_node->callback(remote_address,
682 opp_server_conn_req_node->user_data);
685 int bt_opp_server_initialize(const char *destination,
686 bt_opp_server_push_requested_cb push_requested_cb,
691 if (!destination || !push_requested_cb)
692 return BT_ERROR_INVALID_PARAMETER;
694 if (opp_server_push_node) {
695 ERROR("Already registered");
696 return BT_ERROR_OPERATION_FAILED;
699 opp_server_push_node = g_new0(struct opp_server_push_cb_node, 1);
700 if (opp_server_push_node == NULL) {
702 return BT_ERROR_OUT_OF_MEMORY;
705 ret = bt_opp_register_server(destination,
706 server_push_requested_cb, NULL);
707 if (ret != BT_SUCCESS) {
708 g_free(opp_server_push_node);
709 opp_server_push_node = NULL;
710 return BT_ERROR_OPERATION_FAILED;
713 opp_server_push_node->callback = push_requested_cb;
714 opp_server_push_node->user_data = user_data;
719 int bt_opp_server_initialize_by_connection_request(const char *destination,
720 bt_opp_server_connection_requested_cb connection_requested_cb,
725 if (!destination || !connection_requested_cb)
726 return BT_ERROR_INVALID_PARAMETER;
728 if (opp_server_conn_req_node) {
729 ERROR("Already registered");
730 return BT_ERROR_OPERATION_FAILED;
733 opp_server_conn_req_node =
734 g_new0(struct opp_server_connection_requested_cb, 1);
735 if (opp_server_conn_req_node == NULL) {
737 return BT_ERROR_OUT_OF_MEMORY;
740 ret = bt_opp_register_server(destination,
741 server_connect_requested_cb, NULL);
742 if (ret != BT_SUCCESS) {
743 g_free(opp_server_conn_req_node);
744 opp_server_conn_req_node = NULL;
745 return BT_ERROR_OPERATION_FAILED;
748 opp_server_conn_req_node->callback = connection_requested_cb;
749 opp_server_conn_req_node->user_data = user_data;
754 int bt_opp_server_deinitialize(void)
756 if (opp_server_push_node) {
757 g_free(opp_server_push_node);
758 opp_server_push_node = NULL;
761 if (opp_server_conn_req_node) {
762 g_free(opp_server_conn_req_node);
763 opp_server_conn_req_node = NULL;
766 bt_transfer_progress_cb = NULL;
767 bt_transfer_finished_cb = NULL;
769 return bt_opp_unregister_server();
772 static void bt_opp_server_transfer_state_cb(bt_opp_transfer_state_e state,
773 const char *name, uint64_t size,
774 unsigned char percent, void *user_data)
776 if (state == BT_OPP_TRANSFER_QUEUED ||
777 state == BT_OPP_TRANSFER_ACTIVE)
778 bt_transfer_progress_cb(name, size, percent, user_data);
779 else if (state == BT_OPP_TRANSFER_COMPLETED)
780 bt_transfer_finished_cb(BT_ERROR_NONE, name, size, user_data);
781 else if (state == BT_OPP_TRANSFER_ERROR || BT_OPP_TRANSFER_CANCELED)
782 bt_transfer_finished_cb(BT_ERROR_CANCELLED, name, size, user_data);
786 static void bt_opp_client_transfer_state_cb(unsigned int id,
787 bt_opp_transfer_state_e state,
788 const char *address, const char *name,
789 uint64_t size, unsigned char percent,
794 if (state == BT_OPP_TRANSFER_QUEUED) {
795 DBG("id = %d, name = %s", id, name);
796 if (id == 0 && !g_strcmp0(name, "OBEX_TRANSFER_QUEUED")) {
797 if (bt_push_responded_cb)
798 bt_push_responded_cb(BT_ERROR_NONE,
802 bt_progress_cb(name, size, percent, user_data);
804 } else if (state == BT_OPP_TRANSFER_ACTIVE) {
806 bt_progress_cb(name, size, percent, user_data);
807 } else if (state == BT_OPP_TRANSFER_COMPLETED) {
809 bt_finished_cb(BT_ERROR_NONE, address, user_data);
810 } else if (state == BT_OPP_TRANSFER_ERROR ||
811 state == BT_OPP_TRANSFER_CANCELED ||
812 state == BT_OPP_TRANSFER_UNKNOWN) {
814 bt_finished_cb(BT_ERROR_CANCELLED, address, user_data);
820 int bt_opp_server_accept(bt_opp_server_transfer_progress_cb progress_cb,
821 bt_opp_server_transfer_finished_cb finished_cb,
822 const char *name, void *user_data, int *transfer_id)
824 bt_transfer_progress_cb = progress_cb;
825 bt_transfer_finished_cb = finished_cb;
827 return bt_opp_server_accept_request(name, user_data, transfer_id);
830 int bt_opp_server_reject(void)
832 return bt_opp_server_reject_request();
835 int bt_opp_server_cancel_transfer(int transfer_id)
837 return bt_opp_transfer_cancel(transfer_id);
840 int bt_opp_client_initialize(void)
844 ret = register_opp_object();
845 if (ret != BT_SUCCESS)
848 is_client_register = TRUE;
850 opp_manager_set_service_watch(bt_opp_manager_service_watch,
855 int bt_opp_client_deinitialize(void)
857 is_client_register = FALSE;
859 destroy_opp_object();
861 if (is_server_register)
864 opp_manager_remove_service_watch();
869 int bt_opp_client_add_file(const char *file)
871 int ret = BT_ERROR_NONE;
876 return BT_ERROR_INVALID_PARAMETER;
878 if (access(file, F_OK) != 0) {
879 ret = BT_ERROR_INVALID_PARAMETER;
880 DBG("ret = %d", ret);
884 ret = comms_bluetooth_opp_add_file(file,
885 AGENT_OBJECT_PATH, NULL, NULL);
887 if (ret != BT_ERROR_NONE)
888 return BT_ERROR_OPERATION_FAILED;
892 return BT_ERROR_NONE;
895 int bt_opp_client_clear_files(void)
899 comms_bluetooth_opp_remove_Files(AGENT_OBJECT_PATH, NULL, NULL);
901 return BT_ERROR_NONE;
904 int bt_opp_client_push_files(const char *remote_address,
905 bt_opp_client_push_responded_cb responded_cb,
906 bt_opp_client_push_progress_cb progress_cb,
907 bt_opp_client_push_finished_cb finished_cb,
910 int user_privilieges;
913 if (remote_address == NULL) {
914 DBG("address = NULL");
915 return BT_ERROR_INVALID_PARAMETER;
920 user_privilieges = bt_device_get_privileges(remote_address);
921 if (user_privilieges == 0) {
922 DBG("user not privilieges to pair and use");
923 /*todo: This point will check if Cynara allow user
924 use the remote device
925 if ok, return BT_SUCCESS.
927 memset(pushing_address, 0, ADDRESS_LEN);
928 return BT_ERROR_NOT_ENABLED;
931 memset(pushing_address, 0, ADDRESS_LEN);
932 strcpy(pushing_address, remote_address);
934 bt_push_responded_cb = responded_cb;
935 bt_progress_cb = progress_cb;
936 bt_finished_cb = finished_cb;
937 opp_client_data = user_data;
939 ret = bt_opp_client_push_file(remote_address);
942 DBG("BT_ERROR_NOW_IN_PROGRESS");
943 return BT_ERROR_NOW_IN_PROGRESS;
949 int bt_opp_client_cancel_push(void)
951 int user_privilieges;
953 if (strlen(pushing_address) == 0) {
954 DBG("not need to cancel bonding");
955 return BT_ERROR_NOT_ENABLED;
958 user_privilieges = bt_device_get_privileges(pushing_address);
959 memset(pushing_address, 0, ADDRESS_LEN);
961 if (user_privilieges == 0) {
962 DBG("user not privilieges to pair and use");
963 /*todo: This point will check if Cynara allow user
964 use the remote device
965 if ok, return BT_SUCCESS.
967 return BT_ERROR_NOT_ENABLED;
970 comms_bluetooth_opp_cancel_transfers(NULL, NULL);