2 * Copyright (c) 2017 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.
25 #include "bluetooth-hid-agent.h"
26 #include "bluetooth-hid-manager.h"
28 static GMainLoop *gmain_loop;
29 static char *g_obj_path;
31 static GDBusConnection *gdbus_conn;
32 static GDBusProxy *profile_gproxy;
33 static guint interface_added_sig_id;
34 static guint interface_removed_sig_id;
35 static guint bluez_device_sig_id;
36 static guint name_owner_sig_id;
38 #define BT_HID_SIG_NUM 3
39 #define HID_DEVICE_UUID "00001124-0000-1000-8000-00805f9b43bf"
40 #define DEFAULT_ADAPTER_OBJECT_PATH "/org/bluez/hci0"
42 /*Below Inrospection data is exposed to application from agent*/
43 static const gchar hid_agent_introspection_xml[] =
45 " <interface name='org.tizen.HidApp'>"
46 " <method name='RegisterApplication'>"
48 " <method name='UnregisterApplication'>"
50 " <method name='IsHidConnected'>"
51 " <arg type='b' name='connected' direction='out'/>"
53 " <method name='IsHidConnectable'>"
54 " <arg type='b' name='connectable' direction='out'/>"
59 static bt_hid_agent_info_t bt_hid_info = {0,};
60 static gboolean is_hid_connected;
61 static gboolean is_hid_connectable;
62 static struct sigaction bt_hid_sigoldact[BT_HID_SIG_NUM];
63 static int bt_hid_sig_to_handle[] = { SIGABRT, SIGSEGV, SIGTERM };
65 static void __bt_hid_agent_sigterm_handler(int signo);
66 static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error);
69 static void __on_log_glib(const gchar *log_domain, GLogLevelFlags log_level,
70 const gchar *msg, gpointer user_data)
75 static GQuark __bt_hid_agent_error_quark(void)
80 quark = g_quark_from_static_string("hid-agent");
85 static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error)
87 ERR("error[%d]", error);
90 case BT_HID_AGENT_ERROR_NOT_AVAILABLE:
91 return g_error_new(BT_HID_AGENT_ERROR, error,
92 BT_ERROR_NOT_AVAILABLE);
93 case BT_HID_AGENT_ERROR_NOT_CONNECTED:
94 return g_error_new(BT_HID_AGENT_ERROR, error,
95 BT_ERROR_NOT_CONNECTED);
96 case BT_HID_AGENT_ERROR_CONNECTION_FAILED:
97 return g_error_new(BT_HID_AGENT_ERROR, error,
98 BT_ERROR_NOT_CONNECTION_FAILED);
99 case BT_HID_AGENT_ERROR_BUSY:
100 return g_error_new(BT_HID_AGENT_ERROR, error,
102 case BT_HID_AGENT_ERROR_INVALID_PARAM:
103 return g_error_new(BT_HID_AGENT_ERROR, error,
104 BT_ERROR_INVALID_PARAM);
105 case BT_HID_AGENT_ERROR_ALREADY_EXIST:
106 return g_error_new(BT_HID_AGENT_ERROR, error,
107 BT_ERROR_ALREADY_EXIST);
108 case BT_HID_AGENT_ERROR_ALREADY_CONNECTED:
109 return g_error_new(BT_HID_AGENT_ERROR, error,
110 BT_ERROR_ALREADY_CONNECTED);
111 case BT_HID_AGENT_ERROR_NO_MEMORY:
112 return g_error_new(BT_HID_AGENT_ERROR, error,
114 case BT_HID_AGENT_ERROR_NO_DATA:
115 return g_error_new(BT_HID_AGENT_ERROR, error,
117 case BT_HID_AGENT_ERROR_I_O_ERROR:
118 return g_error_new(BT_HID_AGENT_ERROR, error,
120 case BT_HID_AGENT_ERROR_APPLICATION:
121 return g_error_new(BT_HID_AGENT_ERROR, error,
122 BT_ERROR_OPERATION_NOT_AVAILABLE);
123 case BT_HID_AGENT_ERROR_NOT_ALLOWED:
124 return g_error_new(BT_HID_AGENT_ERROR, error,
125 BT_ERROR_OPERATION_NOT_ALLOWED);
126 case BT_HID_AGENT_ERROR_NOT_SUPPORTED:
127 return g_error_new(BT_HID_AGENT_ERROR, error,
128 BT_ERROR_OPERATION_NOT_SUPPORTED);
129 case BT_HID_AGENT_ERROR_INVALID_FILE_DESCRIPTOR:
130 return g_error_new(BT_HID_AGENT_ERROR, error,
131 BT_ERROR_INVALID_FILE_DESCRIPTOR);
132 case BT_HID_AGENT_ERROR_INTERNAL:
134 return g_error_new(BT_HID_AGENT_ERROR, error,
139 static void __bt_hid_name_owner_changed_cb(GDBusConnection *connection,
140 const gchar *sender_name,
141 const gchar *object_path,
142 const gchar *interface_name,
143 const gchar *signal_name,
144 GVariant *parameters,
147 const char *name_owner = NULL;
148 const char *old_owner = NULL;
149 const char *new_owner = NULL;
152 if (strcasecmp(signal_name, "NameOwnerChanged") == 0) {
153 g_variant_get(parameters, "(&s&s&s)", &name_owner, &old_owner, &new_owner);
155 ret = _bt_hid_register_application(FALSE, name_owner);
156 if (ret == BT_HID_AGENT_ERROR_NONE) {
157 if (_bt_hid_get_sender_list() == NULL) {
158 is_hid_connectable = FALSE;
159 if (name_owner_sig_id > 0) {
160 g_dbus_connection_signal_unsubscribe(gdbus_conn,
162 name_owner_sig_id = 0;
169 static void __hid_agent_method(GDBusConnection *connection,
171 const gchar *object_path,
172 const gchar *interface_name,
173 const gchar *method_name,
174 GVariant *parameters,
175 GDBusMethodInvocation *context,
178 INFO("method %s", method_name);
182 if (g_strcmp0(method_name, "RegisterApplication") == 0) {
183 DBG("Sender = %s\n", sender);
185 ret = _bt_hid_register_application(TRUE, sender);
186 if (ret == BT_HID_AGENT_ERROR_NONE) {
187 is_hid_connectable = TRUE;
188 if (name_owner_sig_id == 0) {
189 name_owner_sig_id = g_dbus_connection_signal_subscribe(gdbus_conn,
190 NULL, NULL, "NameOwnerChanged", NULL, NULL, 0,
191 __bt_hid_name_owner_changed_cb, NULL, NULL);
197 g_dbus_method_invocation_return_value(context, NULL);
198 } else if (g_strcmp0(method_name, "UnregisterApplication") == 0) {
199 DBG("Sender = %s\n", sender);
201 ret = _bt_hid_register_application(FALSE, sender);
202 if (ret == BT_HID_AGENT_ERROR_NONE) {
203 if (_bt_hid_get_sender_list() == NULL) {
204 is_hid_connectable = FALSE;
205 if (name_owner_sig_id > 0) {
206 g_dbus_connection_signal_unsubscribe(gdbus_conn,
208 name_owner_sig_id = 0;
215 g_dbus_method_invocation_return_value(context, NULL);
216 } else if (g_strcmp0(method_name, "IsHidConnected") == 0) {
217 DBG("Going to call IsHidConnected");
218 INFO("is_hid_connected : %s",
219 is_hid_connected ? "Connected" : "Disconnected");
221 g_dbus_method_invocation_return_value(context,
222 g_variant_new("(b)", is_hid_connected));
223 } else if (g_strcmp0(method_name, "IsHidConnectable") == 0) {
224 DBG("Going to call IsHidConnectable");
225 INFO("is_hid_connectable : %s",
226 is_hid_connectable ? "Connectable" : "Non-Connectable");
228 g_dbus_method_invocation_return_value(context,
229 g_variant_new("(b)", is_hid_connectable));
235 err = __bt_hid_agent_set_error(ret);
236 g_dbus_method_invocation_return_gerror(context, err);
240 static const GDBusInterfaceVTable method_table = {
246 static GDBusNodeInfo *__bt_hid_create_method_node_info
247 (const gchar *introspection_data)
249 if (introspection_data == NULL)
252 return g_dbus_node_info_new_for_xml(introspection_data, NULL);
255 static GDBusConnection *__bt_hid_get_gdbus_connection(void)
257 GDBusConnection *local_system_gconn = NULL;
260 if (gdbus_conn == NULL) {
261 gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
264 ERR("Unable to connect to dbus: %s", err->message);
269 } else if (g_dbus_connection_is_closed(gdbus_conn)) {
270 local_system_gconn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
272 if (!local_system_gconn) {
273 ERR("Unable to connect to dbus: %s", err->message);
277 gdbus_conn = local_system_gconn;
282 static gboolean __bt_hid_register_methods(void)
285 GError *error = NULL;
288 GDBusNodeInfo *node_info;
289 GDBusConnection *conn;
291 owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
293 G_BUS_NAME_OWNER_FLAGS_NONE,
297 DBG("owner_id is [%d]", owner_id);
299 conn = __bt_hid_get_gdbus_connection();
301 ERR("Unable to get connection");
305 node_info = __bt_hid_create_method_node_info(hid_agent_introspection_xml);
306 if (node_info == NULL)
309 object_id = g_dbus_connection_register_object(conn,
310 BT_HID_AGENT_OBJECT_PATH,
311 node_info->interfaces[0],
314 g_dbus_node_info_unref(node_info);
315 if (object_id == 0) {
317 ERR("Failed to register: %s", error->message);
327 static GDBusProxy *__bt_hid_gdbus_init_profile_proxy(void)
333 GDBusConnection *conn;
335 conn = __bt_hid_get_gdbus_connection();
337 ERR("Unable to get connection");
341 proxy = g_dbus_proxy_new_sync(conn,
342 G_DBUS_PROXY_FLAGS_NONE, NULL,
343 BLUEZ_SERVICE_NAME, "/org/bluez",
344 BLUEZ_PROFILE_MGMT_INTERFACE, NULL, &err);
348 ERR("Unable to create proxy: %s", err->message);
354 profile_gproxy = proxy;
360 static GDBusProxy *__bt_hid_gdbus_get_profile_proxy(void)
362 return (profile_gproxy) ? profile_gproxy :
363 __bt_hid_gdbus_init_profile_proxy();
366 static GDBusProxy *__bt_hid_gdbus_get_service_proxy(const gchar *service,
367 const gchar *path, const gchar *interface)
373 GDBusConnection *conn;
375 conn = __bt_hid_get_gdbus_connection();
377 ERR("Unable to get connection");
381 proxy = g_dbus_proxy_new_sync(conn,
382 G_DBUS_PROXY_FLAGS_NONE, NULL,
384 interface, NULL, &err);
388 ERR("Unable to create proxy: %s", err->message);
398 static GDBusProxy *__bt_hid_gdbus_get_device_proxy(char *object_path)
400 GDBusConnection *conn;
402 GDBusProxy *device_gproxy;
404 conn = __bt_hid_get_gdbus_connection();
409 device_gproxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
410 NULL, BLUEZ_SERVICE_NAME,
412 BLUEZ_DEVICE_INTERFACE,
415 if (device_gproxy == NULL && err) {
416 ERR("Unable to create proxy: %s", err->message);
421 return device_gproxy;
424 static int __bt_hid_agent_gdbus_method_send(const char *service,
425 GVariant *path, const char *interface,
432 GError *error = NULL;
434 proxy = __bt_hid_gdbus_get_service_proxy(service, g_obj_path, interface);
436 return BT_HID_AGENT_ERROR_INTERNAL;
438 ret = g_dbus_proxy_call_sync(proxy,
440 G_DBUS_CALL_FLAGS_NONE, -1,
443 /* dBUS-RPC is failed */
444 ERR("dBUS-RPC is failed");
446 /* dBUS gives error cause */
447 ERR("D-Bus API failure: errCode[%x], message[%s]",
448 error->code, error->message);
450 g_clear_error(&error);
452 g_object_unref(proxy);
453 return BT_HID_AGENT_ERROR_INTERNAL;
456 g_variant_unref(ret);
457 g_object_unref(proxy);
459 return BT_HID_AGENT_ERROR_NONE;
462 static void __bt_hid_agent_sigterm_handler(int signo)
464 GDBusConnection *conn;
467 ERR_C("***** Signal handler came with signal %d *****", signo);
469 conn = __bt_hid_get_gdbus_connection();
471 ERR("Unable to get G-DBus connection");
475 g_dbus_connection_flush(conn, NULL, NULL, NULL);
476 INFO("Flush g_dbus_connection");
480 g_main_loop_quit(gmain_loop);
484 INFO_C("Terminating HID agent");
488 if (signo == SIGTERM)
491 for (i = 0; i < BT_HID_SIG_NUM; i++)
492 sigaction(bt_hid_sig_to_handle[i], &(bt_hid_sigoldact[i]), NULL);
497 static int __bt_hid_register_profile(const char *uuid,
498 const char *name, const char *object)
503 GError *error = NULL;
504 GVariantBuilder *builder;
506 proxy = __bt_hid_gdbus_get_profile_proxy();
509 return BT_HID_AGENT_ERROR_INTERNAL;
511 builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
513 g_variant_builder_add(builder, "{sv}",
514 "Name", g_variant_new("s",
517 g_variant_builder_add(builder, "{sv}",
518 "PSM", g_variant_new("q", 17));
520 ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile",
521 g_variant_new("(osa{sv})", BT_HID_BLUEZ_OBJECT_PATH,
522 HID_DEVICE_UUID, builder),
523 G_DBUS_CALL_FLAGS_NONE, -1,
526 g_variant_builder_unref(builder);
529 /* dBUS-RPC is failed */
530 ERR("dBUS-RPC is failed");
532 /* dBUS gives error cause */
533 ERR("D-Bus API failure: errCode[%x], message[%s]",
534 error->code, error->message);
535 g_clear_error(&error);
537 return BT_HID_AGENT_ERROR_INTERNAL;
539 g_variant_unref(ret);
542 return BT_HID_AGENT_ERROR_NONE;
545 static void __bt_hid_agent_register(void)
550 ret = __bt_hid_register_profile(HID_DEVICE_UUID,
551 "HID device", BT_HID_BLUEZ_OBJECT_PATH);
553 ERR("Error in register");
559 static void __bt_hid_agent_unregister(void)
564 __bt_hid_agent_gdbus_method_send(BLUEZ_SERVICE_NAME,
565 g_variant_new("(o)", BT_HID_BLUEZ_OBJECT_PATH),
566 BLUEZ_HID_INTERFACE_NAME,
576 static void __bt_hid_agent_filter_cb(GDBusConnection *connection,
577 const gchar *sender_name,
578 const gchar *object_path,
579 const gchar *interface_name,
580 const gchar *signal_name,
581 GVariant *parameters,
587 GVariant *optional_param;
589 if (strcasecmp(signal_name, "InterfacesAdded") == 0) {
591 g_variant_get(parameters, "(&o@a{sa{sv}})",
592 &path, &optional_param);
594 ERR("Invalid adapter path");
598 if (strcasecmp(path, DEFAULT_ADAPTER_OBJECT_PATH) == 0) {
599 g_obj_path = g_strdup(path);
600 INFO("Adapter Path = [%s]", path);
601 __bt_hid_agent_register();
603 } else if (strcasecmp(signal_name, "InterfacesRemoved") == 0) {
604 g_variant_get(parameters, "(&o@as)", &path, &optional_param);
606 __bt_hid_agent_unregister();
611 static void __bt_hid_device_filter_cb(GDBusConnection *connection,
612 const gchar *sender_name,
613 const gchar *object_path,
614 const gchar *interface_name,
615 const gchar *signal_name,
616 GVariant *parameters,
619 DBG("sender_name = %s, object_path = %s", sender_name, object_path);
621 if (strcasecmp(signal_name, "ProfileStateChanged") == 0) {
622 char *profile_uuid = NULL;
623 bt_hid_state_t state = 0;
625 g_variant_get(parameters, "(&si)", &profile_uuid, &state);
626 if (g_strcmp0(profile_uuid, HID_DEVICE_UUID) == 0) {
627 if (state == BT_HID_STATE_CONNECTED) {
628 g_free(bt_hid_info.object_path);
629 bt_hid_info.object_path = g_strdup(object_path);
630 DBG("device : [%s]", bt_hid_info.object_path);
632 _bt_hid_set_profile_state(state);
637 static int __bt_hid_agent_get_adapter_path(GDBusConnection *conn, char *path)
640 GDBusProxy *manager_proxy = NULL;
641 GVariant *result = NULL;
642 char *adapter_path = NULL;
645 return BT_HID_AGENT_ERROR_INTERNAL;
647 manager_proxy = g_dbus_proxy_new_sync(conn,
648 G_DBUS_PROXY_FLAGS_NONE, NULL,
651 BT_MANAGER_INTERFACE,
654 if (!manager_proxy) {
655 ERR("Unable to create proxy: %s", err->message);
659 result = g_dbus_proxy_call_sync(manager_proxy, "DefaultAdapter", NULL,
660 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
663 ERR("Fail to get DefaultAdapter (Error: %s)", err->message);
665 ERR("Fail to get DefaultAdapter");
670 if (g_strcmp0(g_variant_get_type_string(result), "(o)")) {
671 ERR("Incorrect result\n");
675 g_variant_get(result, "(&o)", &adapter_path);
677 if (adapter_path == NULL ||
678 strlen(adapter_path) >= BT_ADAPTER_OBJECT_PATH_MAX) {
679 ERR("Adapter path is inproper\n");
684 g_strlcpy(path, adapter_path, BT_ADAPTER_OBJECT_PATH_MAX);
686 if (g_obj_path == NULL) {
687 g_obj_path = g_strdup(adapter_path);
688 INFO("Update g_obj_path [%s]", adapter_path);
691 g_variant_unref(result);
692 g_object_unref(manager_proxy);
700 g_variant_unref(result);
703 g_object_unref(manager_proxy);
705 return BT_HID_AGENT_ERROR_INTERNAL;
709 static void __bt_hid_agent_dbus_init(void)
711 GDBusConnection *conn;
715 conn = __bt_hid_get_gdbus_connection();
717 ERR("Error in creating the gdbus connection");
720 if (!__bt_hid_register_methods()) {
721 ERR("Error in __bt_hid_register_methods");
725 if (!__bt_hid_agent_get_adapter_path(conn, NULL))
726 __bt_hid_agent_register();
728 interface_added_sig_id = g_dbus_connection_signal_subscribe(conn,
729 NULL, BT_MANAGER_INTERFACE, BT_INTERFACES_ADDED, NULL, NULL, 0,
730 __bt_hid_agent_filter_cb, NULL, NULL);
732 interface_removed_sig_id = g_dbus_connection_signal_subscribe(conn,
733 NULL, BT_MANAGER_INTERFACE, BT_INTERFACES_REMOVED, NULL, NULL, 0,
734 __bt_hid_agent_filter_cb, NULL, NULL);
736 bluez_device_sig_id = g_dbus_connection_signal_subscribe(conn,
737 NULL, BLUEZ_DEVICE_INTERFACE, NULL, NULL,
738 NULL, 0, __bt_hid_device_filter_cb,
741 _bt_hid_set_profile_state(BT_HID_STATE_DISCONNECTED);
746 static void __bt_hid_agent_dbus_deinit(void)
748 if (profile_gproxy) {
749 g_object_unref(profile_gproxy);
750 profile_gproxy = NULL;
754 if (interface_added_sig_id > 0)
755 g_dbus_connection_signal_unsubscribe(gdbus_conn,
756 interface_added_sig_id);
758 if (interface_removed_sig_id > 0)
759 g_dbus_connection_signal_unsubscribe(gdbus_conn,
760 interface_removed_sig_id);
762 if (bluez_device_sig_id > 0)
763 g_dbus_connection_signal_unsubscribe(gdbus_conn,
764 bluez_device_sig_id);
766 if (name_owner_sig_id > 0)
767 g_dbus_connection_signal_unsubscribe(gdbus_conn,
770 interface_added_sig_id = 0;
771 interface_removed_sig_id = 0;
772 bluez_device_sig_id = 0;
773 name_owner_sig_id = 0;
775 g_object_unref(gdbus_conn);
781 bt_hid_agent_error_t _bt_hid_disconnect_profile(void)
784 GDBusProxy *proxy = NULL;
786 if (bt_hid_info.object_path)
787 proxy = __bt_hid_gdbus_get_device_proxy(bt_hid_info.object_path);
790 ERR("Error while getting proxy");
791 return BT_HID_AGENT_ERROR_INTERNAL;
794 /* LCOV_EXCL_START */
795 g_dbus_proxy_call(proxy, "DisconnectProfile",
796 g_variant_new("(s)", HID_DEVICE_UUID),
797 G_DBUS_CALL_FLAGS_NONE, 2000,
802 return BT_HID_AGENT_ERROR_NONE;
806 /* LCOV_EXCL_START */
813 INFO_C("### Starting Bluetooth HID agent");
815 #if GLIB_VERSION_MIN_REQUIRED < GLIB_VERSION_2_36
819 memset(&sa, 0, sizeof(sa));
820 sa.sa_flags = SA_NOCLDSTOP;
821 sa.sa_handler = __bt_hid_agent_sigterm_handler;
823 for (i = 0; i < BT_HID_SIG_NUM; i++)
824 sigaction(bt_hid_sig_to_handle[i], &sa, &(bt_hid_sigoldact[i]));
826 g_log_set_default_handler(__on_log_glib, NULL);
828 gmain_loop = g_main_loop_new(NULL, FALSE);
830 __bt_hid_agent_dbus_init();
832 g_main_loop_run(gmain_loop);
834 __bt_hid_agent_dbus_deinit();
837 g_main_loop_unref(gmain_loop);
839 INFO_C("### Terminating Bluetooth HID agent");