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"
27 static GMainLoop *gmain_loop;
28 static char *g_obj_path;
30 static GDBusConnection *gdbus_conn;
31 static GDBusProxy *profile_gproxy;
32 static guint interface_added_sig_id;
33 static guint interface_removed_sig_id;
34 static guint bluez_device_sig_id;
35 static guint name_owner_sig_id;
37 #define BT_HID_SIG_NUM 3
38 #define HID_DEVICE_UUID "00001124-0000-1000-8000-00805f9b43bf"
39 #define DEFAULT_ADAPTER_OBJECT_PATH "/org/bluez/hci0"
41 /*Below Inrospection data is exposed to application from agent*/
42 static const gchar hid_agent_introspection_xml[] =
44 " <interface name='org.tizen.HidApp'>"
45 " <method name='RegisterApplication'>"
47 " <method name='UnregisterApplication'>"
49 " <method name='IsHidConnected'>"
50 " <arg type='b' name='connected' direction='out'/>"
52 " <method name='IsHidConnectable'>"
53 " <arg type='b' name='connectable' direction='out'/>"
58 static bt_hid_agent_info_t bt_hid_info = {0,};
59 static gboolean is_hid_connected;
60 static gboolean is_hid_connectable;
61 static struct sigaction bt_hid_sigoldact[BT_HID_SIG_NUM];
62 static int bt_hid_sig_to_handle[] = { SIGABRT, SIGSEGV, SIGTERM };
64 static void __bt_hid_agent_sigterm_handler(int signo);
65 static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error);
67 static void __on_log_glib(const gchar *log_domain, GLogLevelFlags log_level,
68 const gchar *msg, gpointer user_data)
73 static GQuark __bt_hid_agent_error_quark(void)
78 quark = g_quark_from_static_string("hid-agent");
83 static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error)
85 ERR("error[%d]", error);
88 case BT_HID_AGENT_ERROR_NOT_AVAILABLE:
89 return g_error_new(BT_HID_AGENT_ERROR, error,
90 BT_ERROR_NOT_AVAILABLE);
91 case BT_HID_AGENT_ERROR_NOT_CONNECTED:
92 return g_error_new(BT_HID_AGENT_ERROR, error,
93 BT_ERROR_NOT_CONNECTED);
94 case BT_HID_AGENT_ERROR_CONNECTION_FAILED:
95 return g_error_new(BT_HID_AGENT_ERROR, error,
96 BT_ERROR_NOT_CONNECTION_FAILED);
97 case BT_HID_AGENT_ERROR_BUSY:
98 return g_error_new(BT_HID_AGENT_ERROR, error,
100 case BT_HID_AGENT_ERROR_INVALID_PARAM:
101 return g_error_new(BT_HID_AGENT_ERROR, error,
102 BT_ERROR_INVALID_PARAM);
103 case BT_HID_AGENT_ERROR_ALREADY_EXIST:
104 return g_error_new(BT_HID_AGENT_ERROR, error,
105 BT_ERROR_ALREADY_EXIST);
106 case BT_HID_AGENT_ERROR_ALREADY_CONNECTED:
107 return g_error_new(BT_HID_AGENT_ERROR, error,
108 BT_ERROR_ALREADY_CONNECTED);
109 case BT_HID_AGENT_ERROR_NO_MEMORY:
110 return g_error_new(BT_HID_AGENT_ERROR, error,
112 case BT_HID_AGENT_ERROR_NO_DATA:
113 return g_error_new(BT_HID_AGENT_ERROR, error,
115 case BT_HID_AGENT_ERROR_I_O_ERROR:
116 return g_error_new(BT_HID_AGENT_ERROR, error,
118 case BT_HID_AGENT_ERROR_APPLICATION:
119 return g_error_new(BT_HID_AGENT_ERROR, error,
120 BT_ERROR_OPERATION_NOT_AVAILABLE);
121 case BT_HID_AGENT_ERROR_NOT_ALLOWED:
122 return g_error_new(BT_HID_AGENT_ERROR, error,
123 BT_ERROR_OPERATION_NOT_ALLOWED);
124 case BT_HID_AGENT_ERROR_NOT_SUPPORTED:
125 return g_error_new(BT_HID_AGENT_ERROR, error,
126 BT_ERROR_OPERATION_NOT_SUPPORTED);
127 case BT_HID_AGENT_ERROR_INVALID_FILE_DESCRIPTOR:
128 return g_error_new(BT_HID_AGENT_ERROR, error,
129 BT_ERROR_INVALID_FILE_DESCRIPTOR);
130 case BT_HID_AGENT_ERROR_INTERNAL:
132 return g_error_new(BT_HID_AGENT_ERROR, error,
137 static void __bt_hid_name_owner_changed_cb(GDBusConnection *connection,
138 const gchar *sender_name,
139 const gchar *object_path,
140 const gchar *interface_name,
141 const gchar *signal_name,
142 GVariant *parameters,
145 char *name_owner = NULL;
146 char *old_owner = NULL;
147 char *new_owner = NULL;
150 if (strcasecmp(signal_name, "NameOwnerChanged") == 0) {
151 g_variant_get(parameters, "(sss)", &name_owner, &old_owner, &new_owner);
153 ret = _bt_hid_register_application(FALSE, name_owner);
154 if (ret == BT_HID_AGENT_ERROR_NONE) {
155 is_hid_connectable = FALSE;
156 if (name_owner_sig_id > 0) {
157 g_dbus_connection_signal_unsubscribe(gdbus_conn,
159 name_owner_sig_id = 0;
165 static void __hid_agent_method(GDBusConnection *connection,
167 const gchar *object_path,
168 const gchar *interface_name,
169 const gchar *method_name,
170 GVariant *parameters,
171 GDBusMethodInvocation *context,
174 INFO("method %s", method_name);
178 if (g_strcmp0(method_name, "RegisterApplication") == 0) {
179 DBG("Sender = %s\n", sender);
181 ret = _bt_hid_register_application(TRUE, sender);
182 if (ret == BT_HID_AGENT_ERROR_NONE) {
183 is_hid_connectable = TRUE;
184 if (name_owner_sig_id == 0) {
185 name_owner_sig_id = g_dbus_connection_signal_subscribe(gdbus_conn,
186 NULL, NULL, "NameOwnerChanged", NULL, NULL, 0,
187 __bt_hid_name_owner_changed_cb, NULL, NULL);
193 g_dbus_method_invocation_return_value(context, NULL);
194 } else if (g_strcmp0(method_name, "UnregisterApplication") == 0) {
195 DBG("Sender = %s\n", sender);
197 ret = _bt_hid_register_application(FALSE, sender);
198 if (ret == BT_HID_AGENT_ERROR_NONE) {
199 is_hid_connectable = FALSE;
200 if (name_owner_sig_id > 0) {
201 g_dbus_connection_signal_unsubscribe(gdbus_conn,
203 name_owner_sig_id = 0;
209 g_dbus_method_invocation_return_value(context, NULL);
210 } else if (g_strcmp0(method_name, "IsHidConnected") == 0) {
211 DBG("Going to call IsHidConnected");
212 INFO("is_hid_connected : %s",
213 is_hid_connected ? "Connected" : "Disconnected");
215 g_dbus_method_invocation_return_value(context,
216 g_variant_new("(b)", is_hid_connected));
217 } else if (g_strcmp0(method_name, "IsHidConnectable") == 0) {
218 DBG("Going to call IsHidConnectable");
219 INFO("is_hid_connectable : %s",
220 is_hid_connectable ? "Connectable" : "Non-Connectable");
222 g_dbus_method_invocation_return_value(context,
223 g_variant_new("(b)", is_hid_connectable));
229 err = __bt_hid_agent_set_error(ret);
230 g_dbus_method_invocation_return_gerror(context, err);
234 static const GDBusInterfaceVTable method_table = {
240 static GDBusNodeInfo *__bt_hid_create_method_node_info
241 (const gchar *introspection_data)
243 if (introspection_data == NULL)
246 return g_dbus_node_info_new_for_xml(introspection_data, NULL);
249 static GDBusConnection *__bt_hid_get_gdbus_connection(void)
251 GDBusConnection *local_system_gconn = NULL;
254 if (gdbus_conn == NULL) {
255 gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
258 ERR("Unable to connect to dbus: %s", err->message);
263 } else if (g_dbus_connection_is_closed(gdbus_conn)) {
264 local_system_gconn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
266 if (!local_system_gconn) {
267 ERR("Unable to connect to dbus: %s", err->message);
271 gdbus_conn = local_system_gconn;
276 static gboolean __bt_hid_register_methods(void)
279 GError *error = NULL;
282 GDBusNodeInfo *node_info;
283 GDBusConnection *conn;
285 owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
287 G_BUS_NAME_OWNER_FLAGS_NONE,
291 DBG("owner_id is [%d]", owner_id);
293 conn = __bt_hid_get_gdbus_connection();
295 ERR("Unable to get connection");
299 node_info = __bt_hid_create_method_node_info(hid_agent_introspection_xml);
300 if (node_info == NULL)
303 object_id = g_dbus_connection_register_object(conn,
304 BT_HID_AGENT_OBJECT_PATH,
305 node_info->interfaces[0],
308 g_dbus_node_info_unref(node_info);
309 if (object_id == 0) {
311 ERR("Failed to register: %s", error->message);
321 static GDBusProxy *__bt_hid_gdbus_init_profile_proxy(void)
327 GDBusConnection *conn;
329 conn = __bt_hid_get_gdbus_connection();
331 ERR("Unable to get connection");
335 proxy = g_dbus_proxy_new_sync(conn,
336 G_DBUS_PROXY_FLAGS_NONE, NULL,
337 BLUEZ_SERVICE_NAME, "/org/bluez",
338 BLUEZ_PROFILE_MGMT_INTERFACE, NULL, &err);
342 ERR("Unable to create proxy: %s", err->message);
348 profile_gproxy = proxy;
354 static GDBusProxy *__bt_hid_gdbus_get_profile_proxy(void)
356 return (profile_gproxy) ? profile_gproxy :
357 __bt_hid_gdbus_init_profile_proxy();
360 static GDBusProxy *__bt_hid_gdbus_get_service_proxy(const gchar *service,
361 const gchar *path, const gchar *interface)
367 GDBusConnection *conn;
369 conn = __bt_hid_get_gdbus_connection();
371 ERR("Unable to get connection");
375 proxy = g_dbus_proxy_new_sync(conn,
376 G_DBUS_PROXY_FLAGS_NONE, NULL,
378 interface, NULL, &err);
382 ERR("Unable to create proxy: %s", err->message);
392 static GDBusProxy *__bt_hid_gdbus_get_device_proxy(char *object_path)
394 GDBusConnection *conn;
396 GDBusProxy *device_gproxy;
398 conn = __bt_hid_get_gdbus_connection();
403 device_gproxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
404 NULL, BLUEZ_SERVICE_NAME,
406 BLUEZ_DEVICE_INTERFACE,
409 if (device_gproxy == NULL && err) {
410 ERR("Unable to create proxy: %s", err->message);
415 return device_gproxy;
418 static int __bt_hid_agent_gdbus_method_send(const char *service,
419 GVariant *path, const char *interface,
426 GError *error = NULL;
428 proxy = __bt_hid_gdbus_get_service_proxy(service, g_obj_path, interface);
430 return BT_HID_AGENT_ERROR_INTERNAL;
432 ret = g_dbus_proxy_call_sync(proxy,
434 G_DBUS_CALL_FLAGS_NONE, -1,
437 /* dBUS-RPC is failed */
438 ERR("dBUS-RPC is failed");
440 /* dBUS gives error cause */
441 ERR("D-Bus API failure: errCode[%x], message[%s]",
442 error->code, error->message);
444 g_clear_error(&error);
446 g_object_unref(proxy);
447 return BT_HID_AGENT_ERROR_INTERNAL;
450 g_variant_unref(ret);
451 g_object_unref(proxy);
453 return BT_HID_AGENT_ERROR_NONE;
456 static void __bt_hid_agent_sigterm_handler(int signo)
458 GDBusConnection *conn;
461 ERR_C("***** Signal handler came with signal %d *****", signo);
463 conn = __bt_hid_get_gdbus_connection();
465 ERR("Unable to get G-DBus connection");
469 g_dbus_connection_flush(conn, NULL, NULL, NULL);
470 INFO("Flush g_dbus_connection");
474 g_main_loop_quit(gmain_loop);
478 INFO_C("Terminating HID agent");
482 if (signo == SIGTERM)
485 for (i = 0; i < BT_HID_SIG_NUM; i++)
486 sigaction(bt_hid_sig_to_handle[i], &(bt_hid_sigoldact[i]), NULL);
491 static int __bt_hid_register_profile(const char *uuid,
492 const char *name, const char *object)
497 GError *error = NULL;
498 GVariantBuilder *builder;
500 proxy = __bt_hid_gdbus_get_profile_proxy();
503 return BT_HID_AGENT_ERROR_INTERNAL;
505 builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
507 g_variant_builder_add(builder, "{sv}",
508 "Name", g_variant_new("s",
511 g_variant_builder_add(builder, "{sv}",
512 "PSM", g_variant_new("q", 17));
514 ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile",
515 g_variant_new("(osa{sv})", BT_HID_BLUEZ_OBJECT_PATH,
516 HID_DEVICE_UUID, builder),
517 G_DBUS_CALL_FLAGS_NONE, -1,
520 g_variant_builder_unref(builder);
523 /* dBUS-RPC is failed */
524 ERR("dBUS-RPC is failed");
526 /* dBUS gives error cause */
527 ERR("D-Bus API failure: errCode[%x], message[%s]",
528 error->code, error->message);
529 g_clear_error(&error);
531 return BT_HID_AGENT_ERROR_INTERNAL;
533 g_variant_unref(ret);
536 return BT_HID_AGENT_ERROR_NONE;
539 static void __bt_hid_agent_register(void)
544 ret = __bt_hid_register_profile(HID_DEVICE_UUID,
545 "HID device", BT_HID_BLUEZ_OBJECT_PATH);
547 ERR("Error in register");
553 static void __bt_hid_agent_unregister(void)
558 __bt_hid_agent_gdbus_method_send(BLUEZ_SERVICE_NAME,
559 g_variant_new("(o)", BT_HID_BLUEZ_OBJECT_PATH),
560 BLUEZ_HID_INTERFACE_NAME,
570 static void __bt_hid_agent_filter_cb(GDBusConnection *connection,
571 const gchar *sender_name,
572 const gchar *object_path,
573 const gchar *interface_name,
574 const gchar *signal_name,
575 GVariant *parameters,
581 GVariant *optional_param;
583 if (strcasecmp(signal_name, "InterfacesAdded") == 0) {
585 g_variant_get(parameters, "(&o@a{sa{sv}})",
586 &path, &optional_param);
588 ERR("Invalid adapter path");
592 if (strcasecmp(path, DEFAULT_ADAPTER_OBJECT_PATH) == 0) {
593 g_obj_path = g_strdup(path);
594 INFO("Adapter Path = [%s]", path);
595 __bt_hid_agent_register();
597 } else if (strcasecmp(signal_name, "InterfacesRemoved") == 0) {
598 g_variant_get(parameters, "(&o@as)", &path, &optional_param);
600 __bt_hid_agent_unregister();
605 void _bt_convert_device_path_to_address(const char *device_path,
606 char *device_address)
608 char address[BT_ADDRESS_STRING_SIZE] = { 0 };
611 ret_if(device_path == NULL);
612 ret_if(device_address == NULL);
614 dev_addr = strstr(device_path, "dev_");
615 if (dev_addr != NULL) {
618 g_strlcpy(address, dev_addr, sizeof(address));
620 while ((pos = strchr(address, '_')) != NULL) {
624 g_strlcpy(device_address, address, BT_ADDRESS_STRING_SIZE);
628 static void __bt_hid_device_filter_cb(GDBusConnection *connection,
629 const gchar *sender_name,
630 const gchar *object_path,
631 const gchar *interface_name,
632 const gchar *signal_name,
633 GVariant *parameters,
636 DBG("sender_name = %s, object_path = %s", sender_name, object_path);
638 if (strcasecmp(signal_name, "ProfileStateChanged") == 0) {
639 char *profile_uuid = NULL;
640 bt_hid_state_t state = 0;
642 g_variant_get(parameters, "(&si)", &profile_uuid, &state);
643 if (g_strcmp0(profile_uuid, HID_DEVICE_UUID) == 0) {
644 if (state == BT_HID_STATE_CONNECTED) {
645 g_free(bt_hid_info.object_path);
646 bt_hid_info.object_path = g_strdup(object_path);
647 DBG("device : [%s]", bt_hid_info.object_path);
649 _bt_hid_set_profile_state(state);
654 static int __bt_hid_agent_get_adapter_path(GDBusConnection *conn, char *path)
657 GDBusProxy *manager_proxy = NULL;
658 GVariant *result = NULL;
659 char *adapter_path = NULL;
662 return BT_HID_AGENT_ERROR_INTERNAL;
664 manager_proxy = g_dbus_proxy_new_sync(conn,
665 G_DBUS_PROXY_FLAGS_NONE, NULL,
668 BT_MANAGER_INTERFACE,
671 if (!manager_proxy) {
672 ERR("Unable to create proxy: %s", err->message);
676 result = g_dbus_proxy_call_sync(manager_proxy, "DefaultAdapter", NULL,
677 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
680 ERR("Fail to get DefaultAdapter (Error: %s)", err->message);
682 ERR("Fail to get DefaultAdapter");
687 if (g_strcmp0(g_variant_get_type_string(result), "(o)")) {
688 ERR("Incorrect result\n");
692 g_variant_get(result, "(&o)", &adapter_path);
694 if (adapter_path == NULL ||
695 strlen(adapter_path) >= BT_ADAPTER_OBJECT_PATH_MAX) {
696 ERR("Adapter path is inproper\n");
701 g_strlcpy(path, adapter_path, BT_ADAPTER_OBJECT_PATH_MAX);
703 if (g_obj_path == NULL) {
704 g_obj_path = g_strdup(adapter_path);
705 INFO("Update g_obj_path [%s]", adapter_path);
708 g_variant_unref(result);
709 g_object_unref(manager_proxy);
717 g_variant_unref(result);
720 g_object_unref(manager_proxy);
722 return BT_HID_AGENT_ERROR_INTERNAL;
726 static void __bt_hid_agent_dbus_init(void)
728 GDBusConnection *conn;
732 conn = __bt_hid_get_gdbus_connection();
734 ERR("Error in creating the gdbus connection");
737 if (!__bt_hid_register_methods()) {
738 ERR("Error in __bt_hid_register_methods");
742 if (!__bt_hid_agent_get_adapter_path(conn, NULL)) {
743 __bt_hid_agent_register();
746 interface_added_sig_id = g_dbus_connection_signal_subscribe(conn,
747 NULL, BT_MANAGER_INTERFACE, BT_INTERFACES_ADDED, NULL, NULL, 0,
748 __bt_hid_agent_filter_cb, NULL, NULL);
750 interface_removed_sig_id = g_dbus_connection_signal_subscribe(conn,
751 NULL, BT_MANAGER_INTERFACE, BT_INTERFACES_REMOVED, NULL, NULL, 0,
752 __bt_hid_agent_filter_cb, NULL, NULL);
754 bluez_device_sig_id = g_dbus_connection_signal_subscribe(conn,
755 NULL, BLUEZ_DEVICE_INTERFACE, NULL, NULL,
756 NULL, 0, __bt_hid_device_filter_cb,
759 _bt_hid_set_profile_state(BT_HID_STATE_DISCONNECTED);
764 static void __bt_hid_agent_dbus_deinit(void)
766 if (profile_gproxy) {
767 g_object_unref(profile_gproxy);
768 profile_gproxy = NULL;
772 if (interface_added_sig_id > 0)
773 g_dbus_connection_signal_unsubscribe(gdbus_conn,
774 interface_added_sig_id);
776 if (interface_removed_sig_id > 0)
777 g_dbus_connection_signal_unsubscribe(gdbus_conn,
778 interface_removed_sig_id);
780 if (bluez_device_sig_id > 0)
781 g_dbus_connection_signal_unsubscribe(gdbus_conn,
782 bluez_device_sig_id);
784 if (name_owner_sig_id > 0)
785 g_dbus_connection_signal_unsubscribe(gdbus_conn,
788 interface_added_sig_id = 0;
789 interface_removed_sig_id = 0;
790 bluez_device_sig_id = 0;
791 name_owner_sig_id = 0;
793 g_object_unref(gdbus_conn);
801 bt_hid_agent_error_t __bt_hid_disconnect_profile(void)
806 proxy = __bt_hid_gdbus_get_device_proxy(bt_hid_info.object_path);
809 ERR("Error while getting proxy");
810 return BT_HID_AGENT_ERROR_INTERNAL;
813 g_dbus_proxy_call(proxy, "DisconnectProfile",
814 g_variant_new("(s)", HID_DEVICE_UUID),
815 G_DBUS_CALL_FLAGS_NONE, 2000,
820 return BT_HID_AGENT_ERROR_NONE;
829 INFO_C("### Starting Bluetooth HID agent");
831 #if GLIB_VERSION_MIN_REQUIRED < GLIB_VERSION_2_36
835 memset(&sa, 0, sizeof(sa));
836 sa.sa_flags = SA_NOCLDSTOP;
837 sa.sa_handler = __bt_hid_agent_sigterm_handler;
839 for (i = 0; i < BT_HID_SIG_NUM; i++)
840 sigaction(bt_hid_sig_to_handle[i], &sa, &(bt_hid_sigoldact[i]));
842 g_log_set_default_handler(__on_log_glib, NULL);
844 gmain_loop = g_main_loop_new(NULL, FALSE);
846 __bt_hid_agent_dbus_init();
848 g_main_loop_run(gmain_loop);
850 __bt_hid_agent_dbus_deinit();
853 g_main_loop_unref(gmain_loop);
855 INFO_C("### Terminating Bluetooth HID agent");