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);
68 static void __on_log_glib(const gchar *log_domain, GLogLevelFlags log_level,
69 const gchar *msg, gpointer user_data)
74 static GQuark __bt_hid_agent_error_quark(void)
79 quark = g_quark_from_static_string("hid-agent");
84 static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error)
86 ERR("error[%d]", error);
89 case BT_HID_AGENT_ERROR_NOT_AVAILABLE:
90 return g_error_new(BT_HID_AGENT_ERROR, error,
91 BT_ERROR_NOT_AVAILABLE);
92 case BT_HID_AGENT_ERROR_NOT_CONNECTED:
93 return g_error_new(BT_HID_AGENT_ERROR, error,
94 BT_ERROR_NOT_CONNECTED);
95 case BT_HID_AGENT_ERROR_CONNECTION_FAILED:
96 return g_error_new(BT_HID_AGENT_ERROR, error,
97 BT_ERROR_NOT_CONNECTION_FAILED);
98 case BT_HID_AGENT_ERROR_BUSY:
99 return g_error_new(BT_HID_AGENT_ERROR, error,
101 case BT_HID_AGENT_ERROR_INVALID_PARAM:
102 return g_error_new(BT_HID_AGENT_ERROR, error,
103 BT_ERROR_INVALID_PARAM);
104 case BT_HID_AGENT_ERROR_ALREADY_EXIST:
105 return g_error_new(BT_HID_AGENT_ERROR, error,
106 BT_ERROR_ALREADY_EXIST);
107 case BT_HID_AGENT_ERROR_ALREADY_CONNECTED:
108 return g_error_new(BT_HID_AGENT_ERROR, error,
109 BT_ERROR_ALREADY_CONNECTED);
110 case BT_HID_AGENT_ERROR_NO_MEMORY:
111 return g_error_new(BT_HID_AGENT_ERROR, error,
113 case BT_HID_AGENT_ERROR_NO_DATA:
114 return g_error_new(BT_HID_AGENT_ERROR, error,
116 case BT_HID_AGENT_ERROR_I_O_ERROR:
117 return g_error_new(BT_HID_AGENT_ERROR, error,
119 case BT_HID_AGENT_ERROR_APPLICATION:
120 return g_error_new(BT_HID_AGENT_ERROR, error,
121 BT_ERROR_OPERATION_NOT_AVAILABLE);
122 case BT_HID_AGENT_ERROR_NOT_ALLOWED:
123 return g_error_new(BT_HID_AGENT_ERROR, error,
124 BT_ERROR_OPERATION_NOT_ALLOWED);
125 case BT_HID_AGENT_ERROR_NOT_SUPPORTED:
126 return g_error_new(BT_HID_AGENT_ERROR, error,
127 BT_ERROR_OPERATION_NOT_SUPPORTED);
128 case BT_HID_AGENT_ERROR_INVALID_FILE_DESCRIPTOR:
129 return g_error_new(BT_HID_AGENT_ERROR, error,
130 BT_ERROR_INVALID_FILE_DESCRIPTOR);
131 case BT_HID_AGENT_ERROR_INTERNAL:
133 return g_error_new(BT_HID_AGENT_ERROR, error,
138 static void __bt_hid_name_owner_changed_cb(GDBusConnection *connection,
139 const gchar *sender_name,
140 const gchar *object_path,
141 const gchar *interface_name,
142 const gchar *signal_name,
143 GVariant *parameters,
146 char *name_owner = NULL;
147 char *old_owner = NULL;
148 char *new_owner = NULL;
151 if (strcasecmp(signal_name, "NameOwnerChanged") == 0) {
152 g_variant_get(parameters, "(sss)", &name_owner, &old_owner, &new_owner);
154 ret = _bt_hid_register_application(FALSE, name_owner);
155 if (ret == BT_HID_AGENT_ERROR_NONE) {
156 if (_bt_hid_get_sender_list() == NULL) {
157 is_hid_connectable = FALSE;
158 if (name_owner_sig_id > 0) {
159 g_dbus_connection_signal_unsubscribe(gdbus_conn,
161 name_owner_sig_id = 0;
168 static void __hid_agent_method(GDBusConnection *connection,
170 const gchar *object_path,
171 const gchar *interface_name,
172 const gchar *method_name,
173 GVariant *parameters,
174 GDBusMethodInvocation *context,
177 INFO("method %s", method_name);
181 if (g_strcmp0(method_name, "RegisterApplication") == 0) {
182 DBG("Sender = %s\n", sender);
184 ret = _bt_hid_register_application(TRUE, sender);
185 if (ret == BT_HID_AGENT_ERROR_NONE) {
186 is_hid_connectable = TRUE;
187 if (name_owner_sig_id == 0) {
188 name_owner_sig_id = g_dbus_connection_signal_subscribe(gdbus_conn,
189 NULL, NULL, "NameOwnerChanged", NULL, NULL, 0,
190 __bt_hid_name_owner_changed_cb, NULL, NULL);
196 g_dbus_method_invocation_return_value(context, NULL);
197 } else if (g_strcmp0(method_name, "UnregisterApplication") == 0) {
198 DBG("Sender = %s\n", sender);
200 ret = _bt_hid_register_application(FALSE, sender);
201 if (ret == BT_HID_AGENT_ERROR_NONE) {
202 if (_bt_hid_get_sender_list() == NULL) {
203 is_hid_connectable = FALSE;
204 if (name_owner_sig_id > 0) {
205 g_dbus_connection_signal_unsubscribe(gdbus_conn,
207 name_owner_sig_id = 0;
214 g_dbus_method_invocation_return_value(context, NULL);
215 } else if (g_strcmp0(method_name, "IsHidConnected") == 0) {
216 DBG("Going to call IsHidConnected");
217 INFO("is_hid_connected : %s",
218 is_hid_connected ? "Connected" : "Disconnected");
220 g_dbus_method_invocation_return_value(context,
221 g_variant_new("(b)", is_hid_connected));
222 } else if (g_strcmp0(method_name, "IsHidConnectable") == 0) {
223 DBG("Going to call IsHidConnectable");
224 INFO("is_hid_connectable : %s",
225 is_hid_connectable ? "Connectable" : "Non-Connectable");
227 g_dbus_method_invocation_return_value(context,
228 g_variant_new("(b)", is_hid_connectable));
234 err = __bt_hid_agent_set_error(ret);
235 g_dbus_method_invocation_return_gerror(context, err);
239 static const GDBusInterfaceVTable method_table = {
245 static GDBusNodeInfo *__bt_hid_create_method_node_info
246 (const gchar *introspection_data)
248 if (introspection_data == NULL)
251 return g_dbus_node_info_new_for_xml(introspection_data, NULL);
254 static GDBusConnection *__bt_hid_get_gdbus_connection(void)
256 GDBusConnection *local_system_gconn = NULL;
259 if (gdbus_conn == NULL) {
260 gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
263 ERR("Unable to connect to dbus: %s", err->message);
268 } else if (g_dbus_connection_is_closed(gdbus_conn)) {
269 local_system_gconn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
271 if (!local_system_gconn) {
272 ERR("Unable to connect to dbus: %s", err->message);
276 gdbus_conn = local_system_gconn;
281 static gboolean __bt_hid_register_methods(void)
284 GError *error = NULL;
287 GDBusNodeInfo *node_info;
288 GDBusConnection *conn;
290 owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
292 G_BUS_NAME_OWNER_FLAGS_NONE,
296 DBG("owner_id is [%d]", owner_id);
298 conn = __bt_hid_get_gdbus_connection();
300 ERR("Unable to get connection");
304 node_info = __bt_hid_create_method_node_info(hid_agent_introspection_xml);
305 if (node_info == NULL)
308 object_id = g_dbus_connection_register_object(conn,
309 BT_HID_AGENT_OBJECT_PATH,
310 node_info->interfaces[0],
313 g_dbus_node_info_unref(node_info);
314 if (object_id == 0) {
316 ERR("Failed to register: %s", error->message);
326 static GDBusProxy *__bt_hid_gdbus_init_profile_proxy(void)
332 GDBusConnection *conn;
334 conn = __bt_hid_get_gdbus_connection();
336 ERR("Unable to get connection");
340 proxy = g_dbus_proxy_new_sync(conn,
341 G_DBUS_PROXY_FLAGS_NONE, NULL,
342 BLUEZ_SERVICE_NAME, "/org/bluez",
343 BLUEZ_PROFILE_MGMT_INTERFACE, NULL, &err);
347 ERR("Unable to create proxy: %s", err->message);
353 profile_gproxy = proxy;
359 static GDBusProxy *__bt_hid_gdbus_get_profile_proxy(void)
361 return (profile_gproxy) ? profile_gproxy :
362 __bt_hid_gdbus_init_profile_proxy();
365 static GDBusProxy *__bt_hid_gdbus_get_service_proxy(const gchar *service,
366 const gchar *path, const gchar *interface)
372 GDBusConnection *conn;
374 conn = __bt_hid_get_gdbus_connection();
376 ERR("Unable to get connection");
380 proxy = g_dbus_proxy_new_sync(conn,
381 G_DBUS_PROXY_FLAGS_NONE, NULL,
383 interface, NULL, &err);
387 ERR("Unable to create proxy: %s", err->message);
397 static GDBusProxy *__bt_hid_gdbus_get_device_proxy(char *object_path)
399 GDBusConnection *conn;
401 GDBusProxy *device_gproxy;
403 conn = __bt_hid_get_gdbus_connection();
408 device_gproxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
409 NULL, BLUEZ_SERVICE_NAME,
411 BLUEZ_DEVICE_INTERFACE,
414 if (device_gproxy == NULL && err) {
415 ERR("Unable to create proxy: %s", err->message);
420 return device_gproxy;
423 static int __bt_hid_agent_gdbus_method_send(const char *service,
424 GVariant *path, const char *interface,
431 GError *error = NULL;
433 proxy = __bt_hid_gdbus_get_service_proxy(service, g_obj_path, interface);
435 return BT_HID_AGENT_ERROR_INTERNAL;
437 ret = g_dbus_proxy_call_sync(proxy,
439 G_DBUS_CALL_FLAGS_NONE, -1,
442 /* dBUS-RPC is failed */
443 ERR("dBUS-RPC is failed");
445 /* dBUS gives error cause */
446 ERR("D-Bus API failure: errCode[%x], message[%s]",
447 error->code, error->message);
449 g_clear_error(&error);
451 g_object_unref(proxy);
452 return BT_HID_AGENT_ERROR_INTERNAL;
455 g_variant_unref(ret);
456 g_object_unref(proxy);
458 return BT_HID_AGENT_ERROR_NONE;
461 static void __bt_hid_agent_sigterm_handler(int signo)
463 GDBusConnection *conn;
466 ERR_C("***** Signal handler came with signal %d *****", signo);
468 conn = __bt_hid_get_gdbus_connection();
470 ERR("Unable to get G-DBus connection");
474 g_dbus_connection_flush(conn, NULL, NULL, NULL);
475 INFO("Flush g_dbus_connection");
479 g_main_loop_quit(gmain_loop);
483 INFO_C("Terminating HID agent");
487 if (signo == SIGTERM)
490 for (i = 0; i < BT_HID_SIG_NUM; i++)
491 sigaction(bt_hid_sig_to_handle[i], &(bt_hid_sigoldact[i]), NULL);
496 static int __bt_hid_register_profile(const char *uuid,
497 const char *name, const char *object)
502 GError *error = NULL;
503 GVariantBuilder *builder;
505 proxy = __bt_hid_gdbus_get_profile_proxy();
508 return BT_HID_AGENT_ERROR_INTERNAL;
510 builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
512 g_variant_builder_add(builder, "{sv}",
513 "Name", g_variant_new("s",
516 g_variant_builder_add(builder, "{sv}",
517 "PSM", g_variant_new("q", 17));
519 ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile",
520 g_variant_new("(osa{sv})", BT_HID_BLUEZ_OBJECT_PATH,
521 HID_DEVICE_UUID, builder),
522 G_DBUS_CALL_FLAGS_NONE, -1,
525 g_variant_builder_unref(builder);
528 /* dBUS-RPC is failed */
529 ERR("dBUS-RPC is failed");
531 /* dBUS gives error cause */
532 ERR("D-Bus API failure: errCode[%x], message[%s]",
533 error->code, error->message);
534 g_clear_error(&error);
536 return BT_HID_AGENT_ERROR_INTERNAL;
538 g_variant_unref(ret);
541 return BT_HID_AGENT_ERROR_NONE;
544 static void __bt_hid_agent_register(void)
549 ret = __bt_hid_register_profile(HID_DEVICE_UUID,
550 "HID device", BT_HID_BLUEZ_OBJECT_PATH);
552 ERR("Error in register");
558 static void __bt_hid_agent_unregister(void)
563 __bt_hid_agent_gdbus_method_send(BLUEZ_SERVICE_NAME,
564 g_variant_new("(o)", BT_HID_BLUEZ_OBJECT_PATH),
565 BLUEZ_HID_INTERFACE_NAME,
575 static void __bt_hid_agent_filter_cb(GDBusConnection *connection,
576 const gchar *sender_name,
577 const gchar *object_path,
578 const gchar *interface_name,
579 const gchar *signal_name,
580 GVariant *parameters,
586 GVariant *optional_param;
588 if (strcasecmp(signal_name, "InterfacesAdded") == 0) {
590 g_variant_get(parameters, "(&o@a{sa{sv}})",
591 &path, &optional_param);
593 ERR("Invalid adapter path");
597 if (strcasecmp(path, DEFAULT_ADAPTER_OBJECT_PATH) == 0) {
598 g_obj_path = g_strdup(path);
599 INFO("Adapter Path = [%s]", path);
600 __bt_hid_agent_register();
602 } else if (strcasecmp(signal_name, "InterfacesRemoved") == 0) {
603 g_variant_get(parameters, "(&o@as)", &path, &optional_param);
605 __bt_hid_agent_unregister();
610 void _bt_convert_device_path_to_address(const char *device_path,
611 char *device_address)
613 char address[BT_ADDRESS_STRING_SIZE] = { 0 };
616 ret_if(device_path == NULL);
617 ret_if(device_address == NULL);
619 dev_addr = strstr(device_path, "dev_");
620 if (dev_addr != NULL) {
623 g_strlcpy(address, dev_addr, sizeof(address));
625 while ((pos = strchr(address, '_')) != NULL) {
629 g_strlcpy(device_address, address, BT_ADDRESS_STRING_SIZE);
633 static void __bt_hid_device_filter_cb(GDBusConnection *connection,
634 const gchar *sender_name,
635 const gchar *object_path,
636 const gchar *interface_name,
637 const gchar *signal_name,
638 GVariant *parameters,
641 DBG("sender_name = %s, object_path = %s", sender_name, object_path);
643 if (strcasecmp(signal_name, "ProfileStateChanged") == 0) {
644 char *profile_uuid = NULL;
645 bt_hid_state_t state = 0;
647 g_variant_get(parameters, "(&si)", &profile_uuid, &state);
648 if (g_strcmp0(profile_uuid, HID_DEVICE_UUID) == 0) {
649 if (state == BT_HID_STATE_CONNECTED) {
650 g_free(bt_hid_info.object_path);
651 bt_hid_info.object_path = g_strdup(object_path);
652 DBG("device : [%s]", bt_hid_info.object_path);
654 _bt_hid_set_profile_state(state);
659 static int __bt_hid_agent_get_adapter_path(GDBusConnection *conn, char *path)
662 GDBusProxy *manager_proxy = NULL;
663 GVariant *result = NULL;
664 char *adapter_path = NULL;
667 return BT_HID_AGENT_ERROR_INTERNAL;
669 manager_proxy = g_dbus_proxy_new_sync(conn,
670 G_DBUS_PROXY_FLAGS_NONE, NULL,
673 BT_MANAGER_INTERFACE,
676 if (!manager_proxy) {
677 ERR("Unable to create proxy: %s", err->message);
681 result = g_dbus_proxy_call_sync(manager_proxy, "DefaultAdapter", NULL,
682 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
685 ERR("Fail to get DefaultAdapter (Error: %s)", err->message);
687 ERR("Fail to get DefaultAdapter");
692 if (g_strcmp0(g_variant_get_type_string(result), "(o)")) {
693 ERR("Incorrect result\n");
697 g_variant_get(result, "(&o)", &adapter_path);
699 if (adapter_path == NULL ||
700 strlen(adapter_path) >= BT_ADAPTER_OBJECT_PATH_MAX) {
701 ERR("Adapter path is inproper\n");
706 g_strlcpy(path, adapter_path, BT_ADAPTER_OBJECT_PATH_MAX);
708 if (g_obj_path == NULL) {
709 g_obj_path = g_strdup(adapter_path);
710 INFO("Update g_obj_path [%s]", adapter_path);
713 g_variant_unref(result);
714 g_object_unref(manager_proxy);
722 g_variant_unref(result);
725 g_object_unref(manager_proxy);
727 return BT_HID_AGENT_ERROR_INTERNAL;
731 static void __bt_hid_agent_dbus_init(void)
733 GDBusConnection *conn;
737 conn = __bt_hid_get_gdbus_connection();
739 ERR("Error in creating the gdbus connection");
742 if (!__bt_hid_register_methods()) {
743 ERR("Error in __bt_hid_register_methods");
747 if (!__bt_hid_agent_get_adapter_path(conn, NULL)) {
748 __bt_hid_agent_register();
751 interface_added_sig_id = g_dbus_connection_signal_subscribe(conn,
752 NULL, BT_MANAGER_INTERFACE, BT_INTERFACES_ADDED, NULL, NULL, 0,
753 __bt_hid_agent_filter_cb, NULL, NULL);
755 interface_removed_sig_id = g_dbus_connection_signal_subscribe(conn,
756 NULL, BT_MANAGER_INTERFACE, BT_INTERFACES_REMOVED, NULL, NULL, 0,
757 __bt_hid_agent_filter_cb, NULL, NULL);
759 bluez_device_sig_id = g_dbus_connection_signal_subscribe(conn,
760 NULL, BLUEZ_DEVICE_INTERFACE, NULL, NULL,
761 NULL, 0, __bt_hid_device_filter_cb,
764 _bt_hid_set_profile_state(BT_HID_STATE_DISCONNECTED);
769 static void __bt_hid_agent_dbus_deinit(void)
771 if (profile_gproxy) {
772 g_object_unref(profile_gproxy);
773 profile_gproxy = NULL;
777 if (interface_added_sig_id > 0)
778 g_dbus_connection_signal_unsubscribe(gdbus_conn,
779 interface_added_sig_id);
781 if (interface_removed_sig_id > 0)
782 g_dbus_connection_signal_unsubscribe(gdbus_conn,
783 interface_removed_sig_id);
785 if (bluez_device_sig_id > 0)
786 g_dbus_connection_signal_unsubscribe(gdbus_conn,
787 bluez_device_sig_id);
789 if (name_owner_sig_id > 0)
790 g_dbus_connection_signal_unsubscribe(gdbus_conn,
793 interface_added_sig_id = 0;
794 interface_removed_sig_id = 0;
795 bluez_device_sig_id = 0;
796 name_owner_sig_id = 0;
798 g_object_unref(gdbus_conn);
806 bt_hid_agent_error_t _bt_hid_disconnect_profile(void)
811 proxy = __bt_hid_gdbus_get_device_proxy(bt_hid_info.object_path);
814 ERR("Error while getting proxy");
815 return BT_HID_AGENT_ERROR_INTERNAL;
818 g_dbus_proxy_call(proxy, "DisconnectProfile",
819 g_variant_new("(s)", HID_DEVICE_UUID),
820 G_DBUS_CALL_FLAGS_NONE, 2000,
825 return BT_HID_AGENT_ERROR_NONE;
834 INFO_C("### Starting Bluetooth HID agent");
836 #if GLIB_VERSION_MIN_REQUIRED < GLIB_VERSION_2_36
840 memset(&sa, 0, sizeof(sa));
841 sa.sa_flags = SA_NOCLDSTOP;
842 sa.sa_handler = __bt_hid_agent_sigterm_handler;
844 for (i = 0; i < BT_HID_SIG_NUM; i++)
845 sigaction(bt_hid_sig_to_handle[i], &sa, &(bt_hid_sigoldact[i]));
847 g_log_set_default_handler(__on_log_glib, NULL);
849 gmain_loop = g_main_loop_new(NULL, FALSE);
851 __bt_hid_agent_dbus_init();
853 g_main_loop_run(gmain_loop);
855 __bt_hid_agent_dbus_deinit();
858 g_main_loop_unref(gmain_loop);
860 INFO_C("### Terminating Bluetooth HID agent");