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)
497 /* TODO: unregister agent */
498 g_free(opp_server.root_folder);
499 opp_server.root_folder = NULL;
501 opp_server.requested_cb = NULL;
502 opp_server.user_data = NULL;
504 is_server_register = FALSE;
507 if (is_client_register)
510 opp_manager_remove_service_watch();
515 int bt_opp_set_transfers_state_cb(bt_opp_transfer_state_cb cb, void *user_data)
517 struct opp_transfer_state_cb_node *state_node;
522 return BT_ERROR_INVALID_PARAMETER;
524 if (opp_transfer_state_node) {
525 DBG("transfer state callback already set.");
526 return BT_ERROR_ALREADY_DONE;
529 state_node = g_new0(struct opp_transfer_state_cb_node, 1);
530 if (state_node == NULL) {
532 return BT_ERROR_OUT_OF_MEMORY;
536 state_node->user_data = user_data;
538 opp_transfer_state_node = state_node;
543 void bt_opp_clear_transfers_state_cb(void)
547 if (!opp_transfer_state_node)
550 g_free(opp_transfer_state_node);
551 opp_transfer_state_node = NULL;
554 int bt_opp_server_accept_request(const char *name, void *user_data,
557 GDBusMethodInvocation *invocation;
560 invocation = opp_server.pending_invocation;
562 if (invocation == NULL) {
563 ERROR("Can't find invocation");
564 return BT_ERROR_OPERATION_FAILED;
567 n = (name != NULL) ? (char *) name : opp_server.pending_name;
569 if (opp_server.root_folder) {
570 file_name = g_build_filename(opp_server.root_folder, n, NULL);
571 g_dbus_method_invocation_return_value(invocation,
572 g_variant_new("(s)", file_name));
575 g_dbus_method_invocation_return_value(invocation,
576 g_variant_new("(s)", n));
578 opp_server.pending_invocation = NULL;
579 opp_server.user_data = user_data;
581 comms_bluetooth_opp_add_notify(opp_server.pending_transfer_path,
584 *transfer_id = opp_server.pending_transfer_id;
586 g_free(opp_server.pending_transfer_path);
587 opp_server.pending_transfer_path = NULL;
588 g_free(opp_server.pending_name);
589 opp_server.pending_name = NULL;
594 int bt_opp_server_reject_request(void)
596 if (opp_server.pending_invocation) {
597 g_dbus_method_invocation_return_dbus_error(
598 opp_server.pending_invocation,
599 OBEX_ERROR_INTERFACE ".Rejected",
602 opp_server.pending_invocation = NULL;
605 opp_server.user_data = NULL;
606 g_free(opp_server.pending_transfer_path);
607 opp_server.pending_transfer_path = NULL;
608 g_free(opp_server.pending_name);
609 opp_server.pending_name = NULL;
614 int bt_opp_transfer_cancel(int transfer_id)
616 comms_bluetooth_opp_cancel_transfer(transfer_id, NULL, NULL);
620 int bt_opp_server_set_destination(const char *dir)
623 return BT_ERROR_INVALID_PARAMETER;
625 if (opp_server.root_folder != NULL)
626 g_free(opp_server.root_folder);
628 opp_server.root_folder = g_strdup(dir);
633 struct opp_push_data{
635 bt_opp_client_push_responded_cb responded_cb;
636 void *responded_data;
637 bt_opp_transfer_state_cb transfer_state_cb;
641 int bt_opp_client_push_file(const char *remote_address)
643 if (remote_address == NULL)
644 return BT_ERROR_INVALID_PARAMETER;
646 return comms_bluetooth_opp_send_file(remote_address,
647 AGENT_OBJECT_PATH, NULL, NULL);
650 /* Deprecate OPP APIs.
651 * Always implement using NEW OPP APIs*/
652 struct opp_server_push_cb_node {
653 bt_opp_server_push_requested_cb callback;
657 struct opp_server_connection_requested_cb {
658 bt_opp_server_connection_requested_cb callback;
662 struct opp_server_push_cb_node *opp_server_push_node;
663 struct opp_server_connection_requested_cb *opp_server_conn_req_node;
664 static bt_opp_server_transfer_progress_cb bt_transfer_progress_cb;
665 static bt_opp_server_transfer_finished_cb bt_transfer_finished_cb;
666 static bt_opp_client_push_progress_cb bt_progress_cb;
667 static bt_opp_client_push_responded_cb bt_push_responded_cb;
668 static bt_opp_client_push_finished_cb bt_finished_cb;
670 void server_push_requested_cb(const char *remote_address, const char *name,
671 uint64_t size, void *user_data)
673 if (opp_server_push_node)
674 opp_server_push_node->callback(name, size,
675 opp_server_push_node->user_data);
678 void server_connect_requested_cb(const char *remote_address, const char *name,
679 uint64_t size, void *user_data)
681 if (opp_server_conn_req_node)
682 opp_server_conn_req_node->callback(remote_address,
683 opp_server_conn_req_node->user_data);
686 int bt_opp_server_initialize(const char *destination,
687 bt_opp_server_push_requested_cb push_requested_cb,
692 if (!destination || !push_requested_cb)
693 return BT_ERROR_INVALID_PARAMETER;
695 if (opp_server_push_node) {
696 ERROR("Already registered");
697 return BT_ERROR_OPERATION_FAILED;
700 opp_server_push_node = g_new0(struct opp_server_push_cb_node, 1);
701 if (opp_server_push_node == NULL) {
703 return BT_ERROR_OUT_OF_MEMORY;
706 ret = bt_opp_register_server(destination,
707 server_push_requested_cb, NULL);
708 if (ret != BT_SUCCESS) {
709 g_free(opp_server_push_node);
710 opp_server_push_node = NULL;
711 return BT_ERROR_OPERATION_FAILED;
714 opp_server_push_node->callback = push_requested_cb;
715 opp_server_push_node->user_data = user_data;
720 int bt_opp_server_initialize_by_connection_request(const char *destination,
721 bt_opp_server_connection_requested_cb connection_requested_cb,
726 if (!destination || !connection_requested_cb)
727 return BT_ERROR_INVALID_PARAMETER;
729 if (opp_server_conn_req_node) {
730 ERROR("Already registered");
731 return BT_ERROR_OPERATION_FAILED;
734 opp_server_conn_req_node =
735 g_new0(struct opp_server_connection_requested_cb, 1);
736 if (opp_server_conn_req_node == NULL) {
738 return BT_ERROR_OUT_OF_MEMORY;
741 ret = bt_opp_register_server(destination,
742 server_connect_requested_cb, NULL);
743 if (ret != BT_SUCCESS) {
744 g_free(opp_server_conn_req_node);
745 opp_server_conn_req_node = NULL;
746 return BT_ERROR_OPERATION_FAILED;
749 opp_server_conn_req_node->callback = connection_requested_cb;
750 opp_server_conn_req_node->user_data = user_data;
755 int bt_opp_server_deinitialize(void)
759 if (opp_server_push_node) {
760 g_free(opp_server_push_node);
761 opp_server_push_node = NULL;
764 if (opp_server_conn_req_node) {
765 g_free(opp_server_conn_req_node);
766 opp_server_conn_req_node = NULL;
769 bt_transfer_progress_cb = NULL;
770 bt_transfer_finished_cb = NULL;
772 return bt_opp_unregister_server();
775 static void bt_opp_server_transfer_state_cb(bt_opp_transfer_state_e state,
776 const char *name, uint64_t size,
777 unsigned char percent, void *user_data)
779 if (state == BT_OPP_TRANSFER_QUEUED ||
780 state == BT_OPP_TRANSFER_ACTIVE)
781 bt_transfer_progress_cb(name, size, percent, user_data);
782 else if (state == BT_OPP_TRANSFER_COMPLETED)
783 bt_transfer_finished_cb(BT_ERROR_NONE, name, size, user_data);
784 else if (state == BT_OPP_TRANSFER_ERROR || BT_OPP_TRANSFER_CANCELED)
785 bt_transfer_finished_cb(BT_ERROR_CANCELLED, name, size, user_data);
789 static void bt_opp_client_transfer_state_cb(unsigned int id,
790 bt_opp_transfer_state_e state,
791 const char *address, const char *name,
792 uint64_t size, unsigned char percent,
797 if (state == BT_OPP_TRANSFER_QUEUED) {
798 DBG("id = %d, name = %s", id, name);
799 if (id == 0 && !g_strcmp0(name, "OBEX_TRANSFER_QUEUED")) {
800 if (bt_push_responded_cb)
801 bt_push_responded_cb(BT_ERROR_NONE,
805 bt_progress_cb(name, size, percent, user_data);
807 } else if (state == BT_OPP_TRANSFER_ACTIVE) {
809 bt_progress_cb(name, size, percent, user_data);
810 } else if (state == BT_OPP_TRANSFER_COMPLETED) {
812 bt_finished_cb(BT_ERROR_NONE, address, user_data);
813 } else if (state == BT_OPP_TRANSFER_ERROR ||
814 state == BT_OPP_TRANSFER_CANCELED ||
815 state == BT_OPP_TRANSFER_UNKNOWN) {
817 bt_finished_cb(BT_ERROR_CANCELLED, address, user_data);
823 int bt_opp_server_accept(bt_opp_server_transfer_progress_cb progress_cb,
824 bt_opp_server_transfer_finished_cb finished_cb,
825 const char *name, void *user_data, int *transfer_id)
827 bt_transfer_progress_cb = progress_cb;
828 bt_transfer_finished_cb = finished_cb;
830 return bt_opp_server_accept_request(name, user_data, transfer_id);
833 int bt_opp_server_reject(void)
835 return bt_opp_server_reject_request();
838 int bt_opp_server_cancel_transfer(int transfer_id)
840 return bt_opp_transfer_cancel(transfer_id);
843 int bt_opp_client_initialize(void)
847 ret = register_opp_object();
848 if (ret != BT_SUCCESS)
851 is_client_register = TRUE;
853 opp_manager_set_service_watch(bt_opp_manager_service_watch,
858 int bt_opp_client_deinitialize(void)
860 is_client_register = FALSE;
862 destroy_opp_object();
864 if (is_server_register)
867 opp_manager_remove_service_watch();
872 int bt_opp_client_add_file(const char *file)
874 int ret = BT_ERROR_NONE;
879 return BT_ERROR_INVALID_PARAMETER;
881 if (access(file, F_OK) != 0) {
882 ret = BT_ERROR_INVALID_PARAMETER;
883 DBG("ret = %d", ret);
887 ret = comms_bluetooth_opp_add_file(file,
888 AGENT_OBJECT_PATH, NULL, NULL);
890 if (ret != BT_ERROR_NONE)
891 return BT_ERROR_OPERATION_FAILED;
895 return BT_ERROR_NONE;
898 int bt_opp_client_clear_files(void)
902 comms_bluetooth_opp_remove_Files(AGENT_OBJECT_PATH, NULL, NULL);
904 return BT_ERROR_NONE;
907 int bt_opp_client_push_files(const char *remote_address,
908 bt_opp_client_push_responded_cb responded_cb,
909 bt_opp_client_push_progress_cb progress_cb,
910 bt_opp_client_push_finished_cb finished_cb,
913 int user_privilieges;
916 if (remote_address == NULL) {
917 DBG("address = NULL");
918 return BT_ERROR_INVALID_PARAMETER;
923 user_privilieges = bt_device_get_privileges(remote_address);
924 if (user_privilieges == 0) {
925 DBG("user not privilieges to pair and use");
926 /*todo: This point will check if Cynara allow user
927 use the remote device
928 if ok, return BT_SUCCESS.
930 memset(pushing_address, 0, ADDRESS_LEN);
931 return BT_ERROR_NOT_ENABLED;
934 memset(pushing_address, 0, ADDRESS_LEN);
935 strcpy(pushing_address, remote_address);
937 bt_push_responded_cb = responded_cb;
938 bt_progress_cb = progress_cb;
939 bt_finished_cb = finished_cb;
940 opp_client_data = user_data;
942 ret = bt_opp_client_push_file(remote_address);
945 DBG("BT_ERROR_NOW_IN_PROGRESS");
946 return BT_ERROR_NOW_IN_PROGRESS;
952 int bt_opp_client_cancel_push(void)
954 int user_privilieges;
956 if (strlen(pushing_address) == 0) {
957 DBG("not need to cancel bonding");
958 return BT_ERROR_NOT_ENABLED;
961 user_privilieges = bt_device_get_privileges(pushing_address);
962 memset(pushing_address, 0, ADDRESS_LEN);
964 if (user_privilieges == 0) {
965 DBG("user not privilieges to pair and use");
966 /*todo: This point will check if Cynara allow user
967 use the remote device
968 if ok, return BT_SUCCESS.
970 return BT_ERROR_NOT_ENABLED;
973 comms_bluetooth_opp_cancel_transfers(NULL, NULL);