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 owner_sig_id;
33 static guint bluez_device_sig_id;
34 static guint name_owner_sig_id;
36 #define BT_HID_SIG_NUM 3
37 #define HID_DEVICE_UUID "00001124-0000-1000-8000-00805f9b43bf"
38 #define DEFAULT_ADAPTER_OBJECT_PATH "/org/bluez/hci0"
40 /*Below Inrospection data is exposed to application from agent*/
41 static const gchar hid_agent_introspection_xml[] =
43 " <interface name='org.tizen.HidApp'>"
44 " <method name='RegisterApplication'>"
46 " <method name='UnregisterApplication'>"
48 " <method name='IsHidConnected'>"
49 " <arg type='b' name='connected' direction='out'/>"
51 " <method name='IsHidConnectable'>"
52 " <arg type='b' name='connectable' direction='out'/>"
57 static bt_hid_agent_info_t bt_hid_info = {0,};
58 static gboolean is_hid_connected;
59 static gboolean is_hid_connectable;
60 static struct sigaction bt_hid_sigoldact[BT_HID_SIG_NUM];
61 static int bt_hid_sig_to_handle[] = { SIGABRT, SIGSEGV, SIGTERM };
63 static void __bt_hid_agent_sigterm_handler(int signo);
64 static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error);
66 static void __on_log_glib(const gchar *log_domain, GLogLevelFlags log_level,
67 const gchar *msg, gpointer user_data)
72 static GQuark __bt_hid_agent_error_quark(void)
77 quark = g_quark_from_static_string("hid-agent");
82 static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error)
84 ERR("error[%d]", error);
87 case BT_HID_AGENT_ERROR_NOT_AVAILABLE:
88 return g_error_new(BT_HID_AGENT_ERROR, error,
89 BT_ERROR_NOT_AVAILABLE);
90 case BT_HID_AGENT_ERROR_NOT_CONNECTED:
91 return g_error_new(BT_HID_AGENT_ERROR, error,
92 BT_ERROR_NOT_CONNECTED);
93 case BT_HID_AGENT_ERROR_CONNECTION_FAILED:
94 return g_error_new(BT_HID_AGENT_ERROR, error,
95 BT_ERROR_NOT_CONNECTION_FAILED);
96 case BT_HID_AGENT_ERROR_BUSY:
97 return g_error_new(BT_HID_AGENT_ERROR, error,
99 case BT_HID_AGENT_ERROR_INVALID_PARAM:
100 return g_error_new(BT_HID_AGENT_ERROR, error,
101 BT_ERROR_INVALID_PARAM);
102 case BT_HID_AGENT_ERROR_ALREADY_EXIST:
103 return g_error_new(BT_HID_AGENT_ERROR, error,
104 BT_ERROR_ALREADY_EXIST);
105 case BT_HID_AGENT_ERROR_ALREADY_CONNECTED:
106 return g_error_new(BT_HID_AGENT_ERROR, error,
107 BT_ERROR_ALREADY_CONNECTED);
108 case BT_HID_AGENT_ERROR_NO_MEMORY:
109 return g_error_new(BT_HID_AGENT_ERROR, error,
111 case BT_HID_AGENT_ERROR_NO_DATA:
112 return g_error_new(BT_HID_AGENT_ERROR, error,
114 case BT_HID_AGENT_ERROR_I_O_ERROR:
115 return g_error_new(BT_HID_AGENT_ERROR, error,
117 case BT_HID_AGENT_ERROR_APPLICATION:
118 return g_error_new(BT_HID_AGENT_ERROR, error,
119 BT_ERROR_OPERATION_NOT_AVAILABLE);
120 case BT_HID_AGENT_ERROR_NOT_ALLOWED:
121 return g_error_new(BT_HID_AGENT_ERROR, error,
122 BT_ERROR_OPERATION_NOT_ALLOWED);
123 case BT_HID_AGENT_ERROR_NOT_SUPPORTED:
124 return g_error_new(BT_HID_AGENT_ERROR, error,
125 BT_ERROR_OPERATION_NOT_SUPPORTED);
126 case BT_HID_AGENT_ERROR_INVALID_FILE_DESCRIPTOR:
127 return g_error_new(BT_HID_AGENT_ERROR, error,
128 BT_ERROR_INVALID_FILE_DESCRIPTOR);
129 case BT_HID_AGENT_ERROR_INTERNAL:
131 return g_error_new(BT_HID_AGENT_ERROR, error,
136 static void __bt_hid_name_owner_changed_cb(GDBusConnection *connection,
137 const gchar *sender_name,
138 const gchar *object_path,
139 const gchar *interface_name,
140 const gchar *signal_name,
141 GVariant *parameters,
144 char *name_owner = NULL;
145 char *old_owner = NULL;
146 char *new_owner = NULL;
149 if (strcasecmp(signal_name, "NameOwnerChanged") == 0) {
150 g_variant_get(parameters, "(sss)", &name_owner, &old_owner, &new_owner);
152 ret = _bt_hid_register_application(FALSE, name_owner);
153 if (ret == BT_HID_AGENT_ERROR_NONE) {
154 is_hid_connectable = FALSE;
155 if (name_owner_sig_id > 0) {
156 g_dbus_connection_signal_unsubscribe(gdbus_conn,
158 name_owner_sig_id = 0;
164 static void __hid_agent_method(GDBusConnection *connection,
166 const gchar *object_path,
167 const gchar *interface_name,
168 const gchar *method_name,
169 GVariant *parameters,
170 GDBusMethodInvocation *context,
173 INFO("method %s", method_name);
177 if (g_strcmp0(method_name, "RegisterApplication") == 0) {
178 DBG("Sender = %s\n", sender);
180 ret = _bt_hid_register_application(TRUE, sender);
181 if (ret == BT_HID_AGENT_ERROR_NONE) {
182 is_hid_connectable = TRUE;
183 if (name_owner_sig_id == 0) {
184 name_owner_sig_id = g_dbus_connection_signal_subscribe(gdbus_conn,
185 NULL, NULL, "NameOwnerChanged", NULL, NULL, 0,
186 __bt_hid_name_owner_changed_cb, NULL, NULL);
192 g_dbus_method_invocation_return_value(context, NULL);
193 } else if (g_strcmp0(method_name, "UnregisterApplication") == 0) {
194 DBG("Sender = %s\n", sender);
196 ret = _bt_hid_register_application(FALSE, sender);
197 if (ret == BT_HID_AGENT_ERROR_NONE) {
198 is_hid_connectable = FALSE;
199 if (name_owner_sig_id > 0) {
200 g_dbus_connection_signal_unsubscribe(gdbus_conn,
202 name_owner_sig_id = 0;
208 g_dbus_method_invocation_return_value(context, NULL);
209 } else if (g_strcmp0(method_name, "IsHidConnected") == 0) {
210 DBG("Going to call IsHidConnected");
211 INFO("is_hid_connected : %s",
212 is_hid_connected ? "Connected" : "Disconnected");
214 g_dbus_method_invocation_return_value(context,
215 g_variant_new("(b)", is_hid_connected));
216 } else if (g_strcmp0(method_name, "IsHidConnectable") == 0) {
217 DBG("Going to call IsHidConnectable");
218 INFO("is_hid_connectable : %s",
219 is_hid_connectable ? "Connectable" : "Non-Connectable");
221 g_dbus_method_invocation_return_value(context,
222 g_variant_new("(b)", is_hid_connectable));
228 err = __bt_hid_agent_set_error(ret);
229 g_dbus_method_invocation_return_gerror(context, err);
233 static const GDBusInterfaceVTable method_table = {
239 static GDBusNodeInfo *__bt_hid_create_method_node_info
240 (const gchar *introspection_data)
242 if (introspection_data == NULL)
245 return g_dbus_node_info_new_for_xml(introspection_data, NULL);
248 static GDBusConnection *__bt_hid_get_gdbus_connection(void)
250 GDBusConnection *local_system_gconn = NULL;
253 if (gdbus_conn == NULL) {
254 gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
257 ERR("Unable to connect to dbus: %s", err->message);
262 } else if (g_dbus_connection_is_closed(gdbus_conn)) {
263 local_system_gconn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
265 if (!local_system_gconn) {
266 ERR("Unable to connect to dbus: %s", err->message);
270 gdbus_conn = local_system_gconn;
275 static gboolean __bt_hid_register_methods(void)
278 GError *error = NULL;
281 GDBusNodeInfo *node_info;
282 GDBusConnection *conn;
284 owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
286 G_BUS_NAME_OWNER_FLAGS_NONE,
290 DBG("owner_id is [%d]", owner_id);
292 conn = __bt_hid_get_gdbus_connection();
294 ERR("Unable to get connection");
298 node_info = __bt_hid_create_method_node_info(hid_agent_introspection_xml);
299 if (node_info == NULL)
302 object_id = g_dbus_connection_register_object(conn,
303 BT_HID_AGENT_OBJECT_PATH,
304 node_info->interfaces[0],
307 g_dbus_node_info_unref(node_info);
308 if (object_id == 0) {
310 ERR("Failed to register: %s", error->message);
320 static GDBusProxy *__bt_hid_gdbus_init_profile_proxy(void)
326 GDBusConnection *conn;
328 conn = __bt_hid_get_gdbus_connection();
330 ERR("Unable to get connection");
334 proxy = g_dbus_proxy_new_sync(conn,
335 G_DBUS_PROXY_FLAGS_NONE, NULL,
336 BLUEZ_SERVICE_NAME, "/org/bluez",
337 BLUEZ_PROFILE_MGMT_INTERFACE, NULL, &err);
341 ERR("Unable to create proxy: %s", err->message);
347 profile_gproxy = proxy;
353 static GDBusProxy *__bt_hid_gdbus_get_profile_proxy(void)
355 return (profile_gproxy) ? profile_gproxy :
356 __bt_hid_gdbus_init_profile_proxy();
359 static GDBusProxy *__bt_hid_gdbus_get_service_proxy(const gchar *service,
360 const gchar *path, const gchar *interface)
366 GDBusConnection *conn;
368 conn = __bt_hid_get_gdbus_connection();
370 ERR("Unable to get connection");
374 proxy = g_dbus_proxy_new_sync(conn,
375 G_DBUS_PROXY_FLAGS_NONE, NULL,
377 interface, NULL, &err);
381 ERR("Unable to create proxy: %s", err->message);
391 static GDBusProxy *__bt_hid_gdbus_get_device_proxy(char *object_path)
393 GDBusConnection *conn;
395 GDBusProxy *device_gproxy;
397 conn = __bt_hid_get_gdbus_connection();
402 device_gproxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
403 NULL, BLUEZ_SERVICE_NAME,
405 BLUEZ_DEVICE_INTERFACE,
408 if (device_gproxy == NULL && err) {
409 ERR("Unable to create proxy: %s", err->message);
414 return device_gproxy;
417 static int __bt_hid_agent_gdbus_method_send(const char *service,
418 GVariant *path, const char *interface,
425 GError *error = NULL;
427 proxy = __bt_hid_gdbus_get_service_proxy(service, g_obj_path, interface);
429 return BT_HID_AGENT_ERROR_INTERNAL;
431 ret = g_dbus_proxy_call_sync(proxy,
433 G_DBUS_CALL_FLAGS_NONE, -1,
436 /* dBUS-RPC is failed */
437 ERR("dBUS-RPC is failed");
439 /* dBUS gives error cause */
440 ERR("D-Bus API failure: errCode[%x], message[%s]",
441 error->code, error->message);
443 g_clear_error(&error);
445 g_object_unref(proxy);
446 return BT_HID_AGENT_ERROR_INTERNAL;
449 g_variant_unref(ret);
450 g_object_unref(proxy);
452 return BT_HID_AGENT_ERROR_NONE;
455 static void __bt_hid_agent_sigterm_handler(int signo)
457 GDBusConnection *conn;
460 ERR_C("***** Signal handler came with signal %d *****", signo);
462 conn = __bt_hid_get_gdbus_connection();
464 ERR("Unable to get G-DBus connection");
468 g_dbus_connection_flush(conn, NULL, NULL, NULL);
469 INFO("Flush g_dbus_connection");
473 g_main_loop_quit(gmain_loop);
477 INFO_C("Terminating HID agent");
481 if (signo == SIGTERM)
484 for (i = 0; i < BT_HID_SIG_NUM; i++)
485 sigaction(bt_hid_sig_to_handle[i], &(bt_hid_sigoldact[i]), NULL);
490 static int __bt_hid_register_profile(const char *uuid,
491 const char *name, const char *object)
496 GError *error = NULL;
497 GVariantBuilder *builder;
499 proxy = __bt_hid_gdbus_get_profile_proxy();
502 return BT_HID_AGENT_ERROR_INTERNAL;
504 builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
506 g_variant_builder_add(builder, "{sv}",
507 "Name", g_variant_new("s",
510 g_variant_builder_add(builder, "{sv}",
511 "PSM", g_variant_new("q", 17));
513 ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile",
514 g_variant_new("(osa{sv})", BT_HID_BLUEZ_OBJECT_PATH,
515 HID_DEVICE_UUID, builder),
516 G_DBUS_CALL_FLAGS_NONE, -1,
519 g_variant_builder_unref(builder);
522 /* dBUS-RPC is failed */
523 ERR("dBUS-RPC is failed");
525 /* dBUS gives error cause */
526 ERR("D-Bus API failure: errCode[%x], message[%s]",
527 error->code, error->message);
528 g_clear_error(&error);
530 return BT_HID_AGENT_ERROR_INTERNAL;
532 g_variant_unref(ret);
535 return BT_HID_AGENT_ERROR_NONE;
538 static void __bt_hid_agent_register(void)
543 ret = __bt_hid_register_profile(HID_DEVICE_UUID,
544 "HID device", BT_HID_BLUEZ_OBJECT_PATH);
546 ERR("Error in register");
552 static void __bt_hid_agent_unregister(void)
557 __bt_hid_agent_gdbus_method_send(BLUEZ_SERVICE_NAME,
558 g_variant_new("(o)", BT_HID_BLUEZ_OBJECT_PATH),
559 BLUEZ_HID_INTERFACE_NAME,
569 static void __bt_hid_agent_filter_cb(GDBusConnection *connection,
570 const gchar *sender_name,
571 const gchar *object_path,
572 const gchar *interface_name,
573 const gchar *signal_name,
574 GVariant *parameters,
580 GVariant *optional_param;
582 if (strcasecmp(signal_name, "InterfacesAdded") == 0) {
584 g_variant_get(parameters, "(&o@a{sa{sv}})",
585 &path, &optional_param);
587 ERR("Invalid adapter path");
591 if (strcasecmp(path, DEFAULT_ADAPTER_OBJECT_PATH) == 0) {
592 g_obj_path = g_strdup(path);
593 INFO("Adapter Path = [%s]", path);
594 __bt_hid_agent_register();
596 } else if (strcasecmp(signal_name, "InterfacesRemoved") == 0) {
597 g_variant_get(parameters, "(&o@as)", &path, &optional_param);
599 __bt_hid_agent_unregister();
604 void _bt_convert_device_path_to_address(const char *device_path,
605 char *device_address)
607 char address[BT_ADDRESS_STRING_SIZE] = { 0 };
610 ret_if(device_path == NULL);
611 ret_if(device_address == NULL);
613 dev_addr = strstr(device_path, "dev_");
614 if (dev_addr != NULL) {
617 g_strlcpy(address, dev_addr, sizeof(address));
619 while ((pos = strchr(address, '_')) != NULL) {
623 g_strlcpy(device_address, address, BT_ADDRESS_STRING_SIZE);
627 static void __bt_hid_device_filter_cb(GDBusConnection *connection,
628 const gchar *sender_name,
629 const gchar *object_path,
630 const gchar *interface_name,
631 const gchar *signal_name,
632 GVariant *parameters,
635 DBG("sender_name = %s, object_path = %s", sender_name, object_path);
637 if (strcasecmp(signal_name, "ProfileStateChanged") == 0) {
638 char *profile_uuid = NULL;
639 bt_hid_state_t state = 0;
641 g_variant_get(parameters, "(&si)", &profile_uuid, &state);
642 if (g_strcmp0(profile_uuid, HID_DEVICE_UUID) == 0) {
643 if (state == BT_HID_STATE_CONNECTED) {
644 g_free(bt_hid_info.object_path);
645 bt_hid_info.object_path = g_strdup(object_path);
646 DBG("device : [%s]", bt_hid_info.object_path);
648 _bt_hid_set_profile_state(state);
653 static int __bt_hid_agent_get_adapter_path(GDBusConnection *conn, char *path)
656 GDBusProxy *manager_proxy = NULL;
657 GVariant *result = NULL;
658 char *adapter_path = NULL;
661 return BT_HID_AGENT_ERROR_INTERNAL;
663 manager_proxy = g_dbus_proxy_new_sync(conn,
664 G_DBUS_PROXY_FLAGS_NONE, NULL,
667 BT_MANAGER_INTERFACE,
670 if (!manager_proxy) {
671 ERR("Unable to create proxy: %s", err->message);
675 result = g_dbus_proxy_call_sync(manager_proxy, "DefaultAdapter", NULL,
676 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
679 ERR("Fail to get DefaultAdapter (Error: %s)", err->message);
681 ERR("Fail to get DefaultAdapter");
686 if (g_strcmp0(g_variant_get_type_string(result), "(o)")) {
687 ERR("Incorrect result\n");
691 g_variant_get(result, "(&o)", &adapter_path);
693 if (adapter_path == NULL ||
694 strlen(adapter_path) >= BT_ADAPTER_OBJECT_PATH_MAX) {
695 ERR("Adapter path is inproper\n");
700 g_strlcpy(path, adapter_path, BT_ADAPTER_OBJECT_PATH_MAX);
702 if (g_obj_path == NULL) {
703 g_obj_path = g_strdup(adapter_path);
704 INFO("Update g_obj_path [%s]", adapter_path);
707 g_variant_unref(result);
708 g_object_unref(manager_proxy);
716 g_variant_unref(result);
719 g_object_unref(manager_proxy);
721 return BT_HID_AGENT_ERROR_INTERNAL;
725 static void __bt_hid_agent_dbus_init(void)
727 GDBusConnection *conn;
731 conn = __bt_hid_get_gdbus_connection();
733 ERR("Error in creating the gdbus connection");
736 if (!__bt_hid_register_methods()) {
737 ERR("Error in __bt_hid_register_methods");
741 if (!__bt_hid_agent_get_adapter_path(conn, NULL)) {
742 __bt_hid_agent_register();
745 owner_sig_id = g_dbus_connection_signal_subscribe(conn,
746 NULL, BT_MANAGER_INTERFACE, NULL, NULL, NULL, 0,
747 __bt_hid_agent_filter_cb, NULL, NULL);
749 bluez_device_sig_id = g_dbus_connection_signal_subscribe(conn,
750 NULL, BLUEZ_DEVICE_INTERFACE, NULL, NULL,
751 NULL, 0, __bt_hid_device_filter_cb,
754 _bt_hid_set_profile_state(BT_HID_STATE_DISCONNECTED);
759 static void __bt_hid_agent_dbus_deinit(void)
761 if (profile_gproxy) {
762 g_object_unref(profile_gproxy);
763 profile_gproxy = NULL;
767 if (owner_sig_id > 0)
768 g_dbus_connection_signal_unsubscribe(gdbus_conn,
771 if (bluez_device_sig_id > 0)
772 g_dbus_connection_signal_unsubscribe(gdbus_conn,
773 bluez_device_sig_id);
775 if (name_owner_sig_id > 0)
776 g_dbus_connection_signal_unsubscribe(gdbus_conn,
780 bluez_device_sig_id = 0;
781 name_owner_sig_id = 0;
783 g_object_unref(gdbus_conn);
791 bt_hid_agent_error_t __bt_hid_disconnect_profile(void)
796 proxy = __bt_hid_gdbus_get_device_proxy(bt_hid_info.object_path);
799 ERR("Error while getting proxy");
800 return BT_HID_AGENT_ERROR_INTERNAL;
803 g_dbus_proxy_call(proxy, "DisconnectProfile",
804 g_variant_new("(s)", HID_DEVICE_UUID),
805 G_DBUS_CALL_FLAGS_NONE, 2000,
810 return BT_HID_AGENT_ERROR_NONE;
819 INFO_C("### Starting Bluetooth HID agent");
821 #if GLIB_VERSION_MIN_REQUIRED < GLIB_VERSION_2_36
825 memset(&sa, 0, sizeof(sa));
826 sa.sa_flags = SA_NOCLDSTOP;
827 sa.sa_handler = __bt_hid_agent_sigterm_handler;
829 for (i = 0; i < BT_HID_SIG_NUM; i++)
830 sigaction(bt_hid_sig_to_handle[i], &sa, &(bt_hid_sigoldact[i]));
832 g_log_set_default_handler(__on_log_glib, NULL);
834 gmain_loop = g_main_loop_new(NULL, FALSE);
836 __bt_hid_agent_dbus_init();
838 g_main_loop_run(gmain_loop);
840 __bt_hid_agent_dbus_deinit();
843 g_main_loop_unref(gmain_loop);
845 INFO_C("### Terminating Bluetooth HID agent");