Add the missed hid-agent folder 42/148242/1 accepted/tizen/unified/20170908.061951 submit/tizen/20170906.230917
authorDoHyun Pyun <dh79.pyun@samsung.com>
Thu, 7 Sep 2017 07:28:33 +0000 (16:28 +0900)
committerDoHyun Pyun <dh79.pyun@samsung.com>
Thu, 7 Sep 2017 07:28:33 +0000 (16:28 +0900)
Change-Id: I9adbf5b6a24cb5df0d6fb98bc938b18f45e433ca
Signed-off-by: DoHyun Pyun <dh79.pyun@samsung.com>
hid-agent/CMakeLists.txt [new file with mode: 0644]
hid-agent/bluetooth-hid-agent.c [new file with mode: 0644]
hid-agent/bluetooth-hid-agent.h [new file with mode: 0644]
hid-agent/bluetooth-hid-manager.c [new file with mode: 0644]
hid-agent/org.bluez.hid_agent.service [new file with mode: 0644]

diff --git a/hid-agent/CMakeLists.txt b/hid-agent/CMakeLists.txt
new file mode 100644 (file)
index 0000000..09f54dc
--- /dev/null
@@ -0,0 +1,25 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(bluetooth-hid-agent C)
+
+SET(SRCS bluetooth-hid-agent.c bluetooth-hid-manager.c)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs_hf_agent
+               REQUIRED
+               dlog aul bluetooth-api capi-appfw-app-manager
+               glib-2.0 gio-2.0 gio-unix-2.0 capi-system-device vconf)
+
+FOREACH(flag ${pkgs_hf_agent_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_hf_agent_LDFLAGS})
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.bluez.hid_agent.service
+               DESTINATION share/dbus-1/system-services)
diff --git a/hid-agent/bluetooth-hid-agent.c b/hid-agent/bluetooth-hid-agent.c
new file mode 100644 (file)
index 0000000..323713e
--- /dev/null
@@ -0,0 +1,848 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <aul.h>
+
+#include "bluetooth-hid-agent.h"
+
+static GMainLoop *gmain_loop;
+static char *g_obj_path;
+
+static GDBusConnection *gdbus_conn;
+static GDBusProxy *profile_gproxy;
+static guint owner_sig_id;
+static guint bluez_device_sig_id;
+static guint name_owner_sig_id;
+
+#define BT_HID_SIG_NUM 3
+#define HID_DEVICE_UUID "00001124-0000-1000-8000-00805f9b43bf"
+#define DEFAULT_ADAPTER_OBJECT_PATH "/org/bluez/hci0"
+
+/*Below Inrospection data is exposed to application from agent*/
+static const gchar hid_agent_introspection_xml[] =
+"<node name='/'>"
+"     <interface name='org.tizen.HidApp'>"
+"              <method name='RegisterApplication'>"
+"              </method>"
+"              <method name='UnregisterApplication'>"
+"              </method>"
+"              <method name='IsHidConnected'>"
+"                      <arg type='b' name='connected' direction='out'/>"
+"              </method>"
+"              <method name='IsHidConnectable'>"
+"                      <arg type='b' name='connectable' direction='out'/>"
+"              </method>"
+"     </interface>"
+"</node>";
+
+static bt_hid_agent_info_t bt_hid_info = {0,};
+static gboolean is_hid_connected;
+static gboolean is_hid_connectable;
+static struct sigaction bt_hid_sigoldact[BT_HID_SIG_NUM];
+static int bt_hid_sig_to_handle[] = { SIGABRT, SIGSEGV, SIGTERM };
+
+static void __bt_hid_agent_sigterm_handler(int signo);
+static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error);
+
+static void __on_log_glib(const gchar *log_domain, GLogLevelFlags log_level,
+               const gchar *msg, gpointer user_data)
+{
+       ERR_C("%s", msg);
+}
+
+static GQuark __bt_hid_agent_error_quark(void)
+{
+       FN_START;
+       static GQuark quark;
+       if (!quark)
+               quark = g_quark_from_static_string("hid-agent");
+
+       return quark;
+}
+
+static GError *__bt_hid_agent_set_error(bt_hid_agent_error_t error)
+{
+       ERR("error[%d]", error);
+
+       switch (error) {
+       case BT_HID_AGENT_ERROR_NOT_AVAILABLE:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_NOT_AVAILABLE);
+       case BT_HID_AGENT_ERROR_NOT_CONNECTED:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_NOT_CONNECTED);
+       case BT_HID_AGENT_ERROR_CONNECTION_FAILED:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_NOT_CONNECTION_FAILED);
+       case BT_HID_AGENT_ERROR_BUSY:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_BUSY);
+       case BT_HID_AGENT_ERROR_INVALID_PARAM:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_INVALID_PARAM);
+       case BT_HID_AGENT_ERROR_ALREADY_EXIST:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_ALREADY_EXIST);
+       case BT_HID_AGENT_ERROR_ALREADY_CONNECTED:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_ALREADY_CONNECTED);
+       case BT_HID_AGENT_ERROR_NO_MEMORY:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_NO_MEMORY);
+       case BT_HID_AGENT_ERROR_NO_DATA:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_NO_DATA);
+       case BT_HID_AGENT_ERROR_I_O_ERROR:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_I_O_ERROR);
+       case BT_HID_AGENT_ERROR_APPLICATION:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_OPERATION_NOT_AVAILABLE);
+       case BT_HID_AGENT_ERROR_NOT_ALLOWED:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_OPERATION_NOT_ALLOWED);
+       case BT_HID_AGENT_ERROR_NOT_SUPPORTED:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_OPERATION_NOT_SUPPORTED);
+       case BT_HID_AGENT_ERROR_INVALID_FILE_DESCRIPTOR:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                       BT_ERROR_INVALID_FILE_DESCRIPTOR);
+       case BT_HID_AGENT_ERROR_INTERNAL:
+       default:
+               return g_error_new(BT_HID_AGENT_ERROR, error,
+                                               BT_ERROR_INTERNAL);
+       }
+}
+
+static void __bt_hid_name_owner_changed_cb(GDBusConnection *connection,
+                                       const gchar *sender_name,
+                                       const gchar *object_path,
+                                       const gchar *interface_name,
+                                       const gchar *signal_name,
+                                       GVariant *parameters,
+                                       gpointer user_data)
+{
+       char *name_owner = NULL;
+       char *old_owner = NULL;
+       char *new_owner = NULL;
+       int ret = 0;
+
+       if (strcasecmp(signal_name, "NameOwnerChanged") == 0) {
+               g_variant_get(parameters, "(sss)", &name_owner, &old_owner, &new_owner);
+
+               ret = _bt_hid_register_application(FALSE, name_owner);
+               if (ret == BT_HID_AGENT_ERROR_NONE) {
+                       is_hid_connectable = FALSE;
+                       if (name_owner_sig_id > 0) {
+                               g_dbus_connection_signal_unsubscribe(gdbus_conn,
+                                                       name_owner_sig_id);
+                               name_owner_sig_id = 0;
+                       }
+               }
+       }
+}
+
+static void __hid_agent_method(GDBusConnection *connection,
+                           const gchar *sender,
+                           const gchar *object_path,
+                           const gchar *interface_name,
+                           const gchar *method_name,
+                           GVariant *parameters,
+                           GDBusMethodInvocation *context,
+                           gpointer user_data)
+{
+       INFO("method %s", method_name);
+       int ret = 0;
+       GError *err;
+
+       if (g_strcmp0(method_name, "RegisterApplication") == 0) {
+               DBG("Sender = %s\n", sender);
+
+               ret = _bt_hid_register_application(TRUE, sender);
+               if (ret == BT_HID_AGENT_ERROR_NONE) {
+                       is_hid_connectable = TRUE;
+                       if (name_owner_sig_id == 0) {
+                               name_owner_sig_id = g_dbus_connection_signal_subscribe(gdbus_conn,
+                                               NULL, NULL, "NameOwnerChanged", NULL, NULL, 0,
+                                               __bt_hid_name_owner_changed_cb, NULL, NULL);
+                       }
+               } else {
+                       goto fail;
+               }
+
+               g_dbus_method_invocation_return_value(context, NULL);
+       } else if (g_strcmp0(method_name, "UnregisterApplication") == 0) {
+               DBG("Sender = %s\n", sender);
+
+               ret = _bt_hid_register_application(FALSE, sender);
+               if (ret == BT_HID_AGENT_ERROR_NONE) {
+                       is_hid_connectable = FALSE;
+                       if (name_owner_sig_id > 0) {
+                               g_dbus_connection_signal_unsubscribe(gdbus_conn,
+                                                       name_owner_sig_id);
+                               name_owner_sig_id = 0;
+                       }
+               } else {
+                       goto fail;
+               }
+
+               g_dbus_method_invocation_return_value(context, NULL);
+       } else if (g_strcmp0(method_name, "IsHidConnected") == 0) {
+               DBG("Going to call IsHidConnected");
+               INFO("is_hid_connected : %s",
+                       is_hid_connected ? "Connected" : "Disconnected");
+
+               g_dbus_method_invocation_return_value(context,
+                               g_variant_new("(b)", is_hid_connected));
+       } else if (g_strcmp0(method_name, "IsHidConnectable") == 0) {
+               DBG("Going to call IsHidConnectable");
+               INFO("is_hid_connectable : %s",
+                       is_hid_connectable ? "Connectable" : "Non-Connectable");
+
+               g_dbus_method_invocation_return_value(context,
+                               g_variant_new("(b)", is_hid_connectable));
+       }
+
+       return;
+
+fail:
+       err = __bt_hid_agent_set_error(ret);
+       g_dbus_method_invocation_return_gerror(context, err);
+       g_error_free(err);
+}
+
+static const GDBusInterfaceVTable method_table = {
+       __hid_agent_method,
+       NULL,
+       NULL,
+};
+
+static GDBusNodeInfo *__bt_hid_create_method_node_info
+                                       (const gchar *introspection_data)
+{
+       if (introspection_data == NULL)
+               return NULL;
+
+       return g_dbus_node_info_new_for_xml(introspection_data, NULL);
+}
+
+static GDBusConnection *__bt_hid_get_gdbus_connection(void)
+{
+       GDBusConnection *local_system_gconn = NULL;
+       GError *err = NULL;
+
+       if (gdbus_conn == NULL) {
+               gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
+               if (!gdbus_conn) {
+                       if (err) {
+                               ERR("Unable to connect to dbus: %s", err->message);
+                               g_clear_error(&err);
+                       }
+                       gdbus_conn = NULL;
+               }
+       } else if (g_dbus_connection_is_closed(gdbus_conn)) {
+               local_system_gconn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
+
+               if (!local_system_gconn) {
+                       ERR("Unable to connect to dbus: %s", err->message);
+                       g_clear_error(&err);
+               }
+
+               gdbus_conn = local_system_gconn;
+       }
+       return gdbus_conn;
+}
+
+static gboolean __bt_hid_register_methods(void)
+{
+       FN_START;
+       GError *error = NULL;
+       guint object_id;
+       guint owner_id;
+       GDBusNodeInfo *node_info;
+       GDBusConnection *conn;
+
+       owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
+                               BT_HID_SERVICE_NAME,
+                               G_BUS_NAME_OWNER_FLAGS_NONE,
+                               NULL, NULL, NULL,
+                               NULL, NULL);
+
+       DBG("owner_id is [%d]", owner_id);
+
+       conn = __bt_hid_get_gdbus_connection();
+       if (!conn) {
+               ERR("Unable to get connection");
+               return FALSE;
+       }
+
+       node_info = __bt_hid_create_method_node_info(hid_agent_introspection_xml);
+       if (node_info == NULL)
+               return FALSE;
+
+       object_id = g_dbus_connection_register_object(conn,
+                                               BT_HID_AGENT_OBJECT_PATH,
+                                               node_info->interfaces[0],
+                                               &method_table,
+                                               NULL, NULL, &error);
+       g_dbus_node_info_unref(node_info);
+       if (object_id == 0) {
+               if (error != NULL) {
+                       ERR("Failed to register: %s", error->message);
+                       g_error_free(error);
+               }
+               return FALSE;
+       }
+
+       FN_END;
+       return TRUE;
+}
+
+static GDBusProxy *__bt_hid_gdbus_init_profile_proxy(void)
+{
+       FN_START;
+
+       GDBusProxy *proxy;
+       GError *err = NULL;
+       GDBusConnection *conn;
+
+       conn = __bt_hid_get_gdbus_connection();
+       if (!conn) {
+               ERR("Unable to get connection");
+               return NULL;
+       }
+
+       proxy =  g_dbus_proxy_new_sync(conn,
+                       G_DBUS_PROXY_FLAGS_NONE, NULL,
+                       BLUEZ_SERVICE_NAME, "/org/bluez",
+                       BLUEZ_PROFILE_MGMT_INTERFACE, NULL, &err);
+
+       if (!proxy) {
+               if (err) {
+                       ERR("Unable to create proxy: %s", err->message);
+                        g_clear_error(&err);
+               }
+               return NULL;
+       }
+
+       profile_gproxy = proxy;
+
+       FN_END;
+       return proxy;
+}
+
+static GDBusProxy *__bt_hid_gdbus_get_profile_proxy(void)
+{
+       return (profile_gproxy) ? profile_gproxy :
+                                 __bt_hid_gdbus_init_profile_proxy();
+}
+
+static GDBusProxy *__bt_hid_gdbus_get_service_proxy(const gchar *service,
+                               const gchar *path, const gchar *interface)
+{
+       FN_START;
+
+       GDBusProxy *proxy;
+       GError *err = NULL;
+       GDBusConnection *conn;
+
+       conn = __bt_hid_get_gdbus_connection();
+       if (!conn) {
+               ERR("Unable to get connection");
+               return NULL;
+       }
+
+       proxy =  g_dbus_proxy_new_sync(conn,
+                       G_DBUS_PROXY_FLAGS_NONE, NULL,
+                       service, path,
+                       interface, NULL, &err);
+
+       if (!proxy) {
+               if (err) {
+                       ERR("Unable to create proxy: %s", err->message);
+                        g_clear_error(&err);
+               }
+               return NULL;
+       }
+
+       FN_END;
+       return proxy;
+}
+
+static GDBusProxy *__bt_hid_gdbus_get_device_proxy(char *object_path)
+{
+       GDBusConnection *conn;
+       GError *err = NULL;
+       GDBusProxy *device_gproxy;
+
+       conn = __bt_hid_get_gdbus_connection();
+
+       if (conn == NULL)
+               return NULL;
+
+       device_gproxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
+                                               NULL, BLUEZ_SERVICE_NAME,
+                                               object_path,
+                                               BLUEZ_DEVICE_INTERFACE,
+                                               NULL, &err);
+
+       if (device_gproxy == NULL && err) {
+               ERR("Unable to create proxy: %s", err->message);
+               g_clear_error(&err);
+               return NULL;
+       }
+
+       return device_gproxy;
+}
+
+static int __bt_hid_agent_gdbus_method_send(const char *service,
+                               GVariant *path, const char *interface,
+                               const char *method)
+{
+       FN_START;
+
+       GVariant *ret;
+       GDBusProxy *proxy;
+       GError *error = NULL;
+
+       proxy = __bt_hid_gdbus_get_service_proxy(service, g_obj_path, interface);
+       if (!proxy)
+               return BT_HID_AGENT_ERROR_INTERNAL;
+
+       ret = g_dbus_proxy_call_sync(proxy,
+                               method, path,
+                               G_DBUS_CALL_FLAGS_NONE, -1,
+                               NULL, &error);
+       if (ret == NULL) {
+               /* dBUS-RPC is failed */
+               ERR("dBUS-RPC is failed");
+               if (error != NULL) {
+                       /* dBUS gives error cause */
+                       ERR("D-Bus API failure: errCode[%x], message[%s]",
+                              error->code, error->message);
+
+                       g_clear_error(&error);
+               }
+               g_object_unref(proxy);
+               return BT_HID_AGENT_ERROR_INTERNAL;
+       }
+
+       g_variant_unref(ret);
+       g_object_unref(proxy);
+
+       return BT_HID_AGENT_ERROR_NONE;
+}
+
+static void __bt_hid_agent_sigterm_handler(int signo)
+{
+       GDBusConnection *conn;
+       int i;
+
+       ERR_C("***** Signal handler came with signal %d *****", signo);
+
+       conn = __bt_hid_get_gdbus_connection();
+       if (!conn) {
+               ERR("Unable to get G-DBus connection");
+               goto done;
+       }
+
+       g_dbus_connection_flush(conn, NULL, NULL, NULL);
+       INFO("Flush g_dbus_connection");
+
+done:
+       if (gmain_loop) {
+               g_main_loop_quit(gmain_loop);
+               INFO("Exiting");
+               gmain_loop = NULL;
+       } else {
+               INFO_C("Terminating HID agent");
+               exit(0);
+       }
+
+       if (signo == SIGTERM)
+               return;
+
+       for (i = 0; i < BT_HID_SIG_NUM; i++)
+               sigaction(bt_hid_sig_to_handle[i], &(bt_hid_sigoldact[i]), NULL);
+
+       raise(signo);
+}
+
+static int __bt_hid_register_profile(const char *uuid,
+                const char *name, const char *object)
+{
+       FN_START;
+       GDBusProxy *proxy;
+       GVariant *ret;
+       GError *error = NULL;
+       GVariantBuilder *builder;
+
+       proxy = __bt_hid_gdbus_get_profile_proxy();
+
+       if (proxy == NULL)
+               return BT_HID_AGENT_ERROR_INTERNAL;
+
+       builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+       g_variant_builder_add(builder, "{sv}",
+                       "Name", g_variant_new("s",
+                       name));
+
+       g_variant_builder_add(builder, "{sv}",
+                       "PSM", g_variant_new("q", 17));
+
+       ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile",
+                               g_variant_new("(osa{sv})", BT_HID_BLUEZ_OBJECT_PATH,
+                                       HID_DEVICE_UUID, builder),
+                               G_DBUS_CALL_FLAGS_NONE, -1,
+                               NULL, &error);
+
+       g_variant_builder_unref(builder);
+
+       if (ret == NULL) {
+               /* dBUS-RPC is failed */
+               ERR("dBUS-RPC is failed");
+               if (error != NULL) {
+                       /* dBUS gives error cause */
+                       ERR("D-Bus API failure: errCode[%x], message[%s]",
+                               error->code, error->message);
+                       g_clear_error(&error);
+               }
+               return BT_HID_AGENT_ERROR_INTERNAL;
+       }
+       g_variant_unref(ret);
+
+       FN_END;
+       return BT_HID_AGENT_ERROR_NONE;
+}
+
+static void __bt_hid_agent_register(void)
+{
+       FN_START;
+       int ret;
+
+       ret = __bt_hid_register_profile(HID_DEVICE_UUID,
+                                       "HID device", BT_HID_BLUEZ_OBJECT_PATH);
+       if (ret)
+               ERR("Error in register");
+
+       FN_END;
+       return;
+}
+
+static void __bt_hid_agent_unregister(void)
+{
+       FN_START;
+
+       if (g_obj_path) {
+               __bt_hid_agent_gdbus_method_send(BLUEZ_SERVICE_NAME,
+                                               g_variant_new("(o)", BT_HID_BLUEZ_OBJECT_PATH),
+                                               BLUEZ_HID_INTERFACE_NAME,
+                                               "UnregisterAgent");
+               g_free(g_obj_path);
+               g_obj_path = NULL;
+       }
+
+       FN_END;
+       return;
+}
+
+static void __bt_hid_agent_filter_cb(GDBusConnection *connection,
+                                                const gchar *sender_name,
+                                                const gchar *object_path,
+                                                const gchar *interface_name,
+                                                const gchar *signal_name,
+                                                GVariant *parameters,
+                                                gpointer user_data)
+{
+       FN_START;
+       char *path = NULL;
+
+       GVariant *optional_param;
+
+       if (strcasecmp(signal_name, "InterfacesAdded") == 0) {
+
+               g_variant_get(parameters, "(&o@a{sa{sv}})",
+                               &path, &optional_param);
+               if (!path) {
+                       ERR("Invalid adapter path");
+                       return;
+               }
+
+               if (strcasecmp(path, DEFAULT_ADAPTER_OBJECT_PATH) == 0) {
+                       g_obj_path = g_strdup(path);
+                       INFO("Adapter Path = [%s]", path);
+                       __bt_hid_agent_register();
+               }
+       } else if (strcasecmp(signal_name, "InterfacesRemoved") == 0) {
+               g_variant_get(parameters, "(&o@as)", &path, &optional_param);
+               if (!path)
+                       __bt_hid_agent_unregister();
+       }
+       FN_END;
+}
+
+void _bt_convert_device_path_to_address(const char *device_path,
+                                               char *device_address)
+{
+       char address[BT_ADDRESS_STRING_SIZE] = { 0 };
+       char *dev_addr;
+
+       ret_if(device_path == NULL);
+       ret_if(device_address == NULL);
+
+       dev_addr = strstr(device_path, "dev_");
+       if (dev_addr != NULL) {
+               char *pos = NULL;
+               dev_addr += 4;
+               g_strlcpy(address, dev_addr, sizeof(address));
+
+               while ((pos = strchr(address, '_')) != NULL) {
+                       *pos = ':';
+               }
+
+               g_strlcpy(device_address, address, BT_ADDRESS_STRING_SIZE);
+       }
+}
+
+static void __bt_hid_device_filter_cb(GDBusConnection *connection,
+                                       const gchar *sender_name,
+                                       const gchar *object_path,
+                                       const gchar *interface_name,
+                                       const gchar *signal_name,
+                                       GVariant *parameters,
+                                       gpointer user_data)
+{
+       DBG("sender_name = %s, object_path = %s", sender_name, object_path);
+
+        if (strcasecmp(signal_name, "ProfileStateChanged") == 0) {
+               char *profile_uuid = NULL;
+               bt_hid_state_t state = 0;
+
+               g_variant_get(parameters, "(&si)", &profile_uuid, &state);
+               if (g_strcmp0(profile_uuid, HID_DEVICE_UUID) == 0) {
+                       if (state == BT_HID_STATE_CONNECTED) {
+                               g_free(bt_hid_info.object_path);
+                               bt_hid_info.object_path = g_strdup(object_path);
+                               DBG("device : [%s]", bt_hid_info.object_path);
+                       }
+                       _bt_hid_set_profile_state(state);
+               }
+       }
+}
+
+static int __bt_hid_agent_get_adapter_path(GDBusConnection *conn, char *path)
+{
+       GError *err = NULL;
+       GDBusProxy *manager_proxy = NULL;
+       GVariant *result = NULL;
+       char *adapter_path = NULL;
+
+       if (conn == NULL)
+               return BT_HID_AGENT_ERROR_INTERNAL;
+
+       manager_proxy =  g_dbus_proxy_new_sync(conn,
+                       G_DBUS_PROXY_FLAGS_NONE, NULL,
+                       BLUEZ_SERVICE_NAME,
+                       "/",
+                       BT_MANAGER_INTERFACE,
+                       NULL, &err);
+
+       if (!manager_proxy) {
+               ERR("Unable to create proxy: %s", err->message);
+               goto fail;
+       }
+
+       result = g_dbus_proxy_call_sync(manager_proxy, "DefaultAdapter", NULL,
+                       G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
+       if (!result) {
+               if (err != NULL)
+                       ERR("Fail to get DefaultAdapter (Error: %s)", err->message);
+               else
+                       ERR("Fail to get DefaultAdapter");
+
+               goto fail;
+       }
+
+       if (g_strcmp0(g_variant_get_type_string(result), "(o)")) {
+               ERR("Incorrect result\n");
+               goto fail;
+       }
+
+       g_variant_get(result, "(&o)", &adapter_path);
+
+       if (adapter_path == NULL ||
+               strlen(adapter_path) >= BT_ADAPTER_OBJECT_PATH_MAX) {
+               ERR("Adapter path is inproper\n");
+               goto fail;
+       }
+
+       if (path)
+               g_strlcpy(path, adapter_path, BT_ADAPTER_OBJECT_PATH_MAX);
+
+       if (g_obj_path == NULL) {
+               g_obj_path = g_strdup(adapter_path);
+               INFO("Update g_obj_path [%s]", adapter_path);
+       }
+
+       g_variant_unref(result);
+       g_object_unref(manager_proxy);
+
+       return 0;
+
+fail:
+       g_clear_error(&err);
+
+       if (result)
+               g_variant_unref(result);
+
+       if (manager_proxy)
+               g_object_unref(manager_proxy);
+
+       return BT_HID_AGENT_ERROR_INTERNAL;
+
+}
+
+static void __bt_hid_agent_dbus_init(void)
+{
+       GDBusConnection *conn;
+
+       FN_START;
+
+       conn = __bt_hid_get_gdbus_connection();
+       if (conn == NULL) {
+               ERR("Error in creating the gdbus connection");
+               return;
+       }
+       if (!__bt_hid_register_methods()) {
+               ERR("Error in __bt_hid_register_methods");
+               return;
+       }
+
+       if (!__bt_hid_agent_get_adapter_path(conn, NULL)) {
+               __bt_hid_agent_register();
+       }
+
+       owner_sig_id = g_dbus_connection_signal_subscribe(conn,
+                               NULL, BT_MANAGER_INTERFACE, NULL, NULL, NULL, 0,
+                               __bt_hid_agent_filter_cb, NULL, NULL);
+
+       bluez_device_sig_id = g_dbus_connection_signal_subscribe(conn,
+                               NULL, BLUEZ_DEVICE_INTERFACE, NULL, NULL,
+                               NULL, 0, __bt_hid_device_filter_cb,
+                               NULL, NULL);
+
+       _bt_hid_set_profile_state(BT_HID_STATE_DISCONNECTED);
+
+       FN_END;
+}
+
+static void __bt_hid_agent_dbus_deinit(void)
+{
+       if (profile_gproxy) {
+               g_object_unref(profile_gproxy);
+               profile_gproxy = NULL;
+       }
+
+       if (gdbus_conn) {
+               if (owner_sig_id > 0)
+                       g_dbus_connection_signal_unsubscribe(gdbus_conn,
+                                               owner_sig_id);
+
+               if (bluez_device_sig_id > 0)
+                       g_dbus_connection_signal_unsubscribe(gdbus_conn,
+                                               bluez_device_sig_id);
+
+               if (name_owner_sig_id > 0)
+                       g_dbus_connection_signal_unsubscribe(gdbus_conn,
+                                               name_owner_sig_id);
+
+               owner_sig_id = 0;
+               bluez_device_sig_id = 0;
+               name_owner_sig_id = 0;
+
+               g_object_unref(gdbus_conn);
+               gdbus_conn = NULL;
+       }
+}
+
+
+
+
+bt_hid_agent_error_t __bt_hid_disconnect_profile(void)
+{
+       FN_START;
+       GDBusProxy *proxy;
+
+       proxy = __bt_hid_gdbus_get_device_proxy(bt_hid_info.object_path);
+
+       if (proxy == NULL) {
+               ERR("Error while getting proxy");
+               return BT_HID_AGENT_ERROR_INTERNAL;
+       }
+
+       g_dbus_proxy_call(proxy, "DisconnectProfile",
+                       g_variant_new("(s)", HID_DEVICE_UUID),
+                       G_DBUS_CALL_FLAGS_NONE, 2000,
+                       NULL, NULL, NULL);
+
+       FN_END;
+
+       return BT_HID_AGENT_ERROR_NONE;
+}
+
+int main(void)
+{
+       int i;
+       struct sigaction sa;
+
+
+       INFO_C("### Starting Bluetooth HID agent");
+
+#if GLIB_VERSION_MIN_REQUIRED < GLIB_VERSION_2_36
+       g_type_init();
+#endif
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags = SA_NOCLDSTOP;
+       sa.sa_handler = __bt_hid_agent_sigterm_handler;
+
+       for (i = 0; i < BT_HID_SIG_NUM; i++)
+               sigaction(bt_hid_sig_to_handle[i], &sa, &(bt_hid_sigoldact[i]));
+
+       g_log_set_default_handler(__on_log_glib, NULL);
+
+       gmain_loop = g_main_loop_new(NULL, FALSE);
+
+       __bt_hid_agent_dbus_init();
+
+       g_main_loop_run(gmain_loop);
+
+       __bt_hid_agent_dbus_deinit();
+
+       if (gmain_loop)
+               g_main_loop_unref(gmain_loop);
+
+       INFO_C("### Terminating Bluetooth HID agent");
+       return 0;
+}
+
diff --git a/hid-agent/bluetooth-hid-agent.h b/hid-agent/bluetooth-hid-agent.h
new file mode 100644 (file)
index 0000000..8149ce6
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Bluetooth-hid-agent
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __DEF_BT_HID_AGENT_H_
+#define __DEF_BT_HID_AGENT_H_
+#define FUNCTION_TRACE
+
+#include <stdio.h>
+#include <dlog.h>
+#include <glib.h>
+#include <sys/socket.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <inttypes.h>
+
+#undef LOG_TAG
+#define LOG_TAG "BLUETOOTH_HID_AGENT"
+
+#define LOG_COLOR_RESET    "\033[0m"
+#define LOG_COLOR_RED      "\033[31m"
+#define LOG_COLOR_YELLOW   "\033[33m"
+#define LOG_COLOR_GREEN         "\033[32m"
+#define LOG_COLOR_BLUE          "\033[36m"
+#define LOG_COLOR_PURPLE   "\033[35m"
+
+#define DBG(fmt, args...) SLOGD(fmt, ##args)
+#define INFO(fmt, args...) SLOGI(fmt, ##args)
+#define ERR(fmt, args...) SLOGE(fmt, ##args)
+#define DBG_SECURE(fmt, args...) SECURE_SLOGD(fmt, ##args)
+#define INFO_SECURE(fmt, args...) SECURE_SLOGI(fmt, ##args)
+#define ERR_SECURE(fmt, args...) SECURE_SLOGE(fmt, ##args)
+#define DBG_SECURE(fmt, args...) SECURE_SLOGD(fmt, ##args)
+#define INFO_C(fmt, arg...) \
+       SLOGI_IF(TRUE,  LOG_COLOR_BLUE" "fmt" "LOG_COLOR_RESET, ##arg)
+#define ERR_C(fmt, arg...) \
+       SLOGI_IF(TRUE,  LOG_COLOR_RED" "fmt" "LOG_COLOR_RESET, ##arg)
+
+#ifdef FUNCTION_TRACE
+#define        FN_START DBG("[ENTER FUNC]")
+#define        FN_END DBG("[EXIT FUNC]")
+#else
+#define        FN_START
+#define        FN_END
+#endif
+
+#define BT_HID_SERVICE_NAME "org.bluez.hid_agent"
+#define BT_HID_AGENT_OBJECT_PATH "/org/bluez/hid_agent"
+#define BT_HID_SERVICE_INTERFACE "org.tizen.HidApp"
+#define BT_HID_AGENT_ERROR (__bt_hid_agent_error_quark())
+
+#define BT_HID_BLUEZ_OBJECT_PATH "/org/tizen/hid"
+#define BT_HID_BLUEZ_INTERFACE "org.bluez.HidAgent"
+
+#define BLUEZ_SERVICE_NAME "org.bluez"
+#define BLUEZ_HID_INTERFACE_NAME "org.bluez.hid_device"
+
+#define BLUEZ_PROFILE_MGMT_INTERFACE "org.bluez.ProfileManager1"
+#define BT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager"
+#define BT_ADAPTER_INTERFACE   "org.bluez.Adapter1"
+
+#define BT_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
+#define BLUEZ_MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device1"
+#define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource"
+
+#define BT_ADDRESS_STRING_SIZE 18
+#define BT_ADAPTER_OBJECT_PATH_MAX 50
+#define BT_HID_DATA_BUF_SIZE 1024
+
+#define BT_ERROR_INTERNAL "InternalError"
+#define BT_ERROR_NOT_AVAILABLE "NotAvailable"
+#define BT_ERROR_NOT_CONNECTED "NotConnected"
+#define BT_ERROR_NOT_CONNECTION_FAILED "ConnectionFailed"
+#define BT_ERROR_BUSY "InProgress"
+#define BT_ERROR_INVALID_PARAM "InvalidArguments"
+#define BT_ERROR_ALREADY_EXIST "AlreadyExists"
+#define BT_ERROR_ALREADY_CONNECTED "Already Connected"
+#define BT_ERROR_NO_MEMORY "No memory"
+#define BT_ERROR_NO_DATA "No data"
+#define BT_ERROR_I_O_ERROR "I/O error"
+#define BT_ERROR_OPERATION_NOT_AVAILABLE "Operation currently not available"
+#define BT_ERROR_OPERATION_NOT_ALLOWED "Operation not allowed"
+#define BT_ERROR_OPERATION_NOT_SUPPORTED "Operation not supported"
+#define BT_ERROR_INVALID_FILE_DESCRIPTOR "Invalid File Descriptor"
+
+#define ret_if(expr) \
+       do { \
+               if (expr) { \
+                       ERR("(%s) return", #expr); \
+                       return; \
+               } \
+       } while (0)
+
+typedef enum {
+       BT_HID_AGENT_ERROR_NONE,
+       BT_HID_AGENT_ERROR_INTERNAL,
+       BT_HID_AGENT_ERROR_NOT_AVAILABLE,
+       BT_HID_AGENT_ERROR_NOT_CONNECTED,
+       BT_HID_AGENT_ERROR_CONNECTION_FAILED,
+       BT_HID_AGENT_ERROR_BUSY,
+       BT_HID_AGENT_ERROR_INVALID_PARAM,
+       BT_HID_AGENT_ERROR_ALREADY_EXIST,
+       BT_HID_AGENT_ERROR_ALREADY_CONNECTED,
+       BT_HID_AGENT_ERROR_NO_MEMORY,
+       BT_HID_AGENT_ERROR_NO_DATA,
+       BT_HID_AGENT_ERROR_I_O_ERROR,
+       BT_HID_AGENT_ERROR_APPLICATION,
+       BT_HID_AGENT_ERROR_NOT_ALLOWED,
+       BT_HID_AGENT_ERROR_NOT_SUPPORTED,
+       BT_HID_AGENT_ERROR_INVALID_FILE_DESCRIPTOR,
+} bt_hid_agent_error_t;
+
+typedef enum {
+       BT_HID_STATE_UNAVAILABLE,
+       BT_HID_STATE_DISCONNECTED,
+       BT_HID_STATE_CONNECTING,
+       BT_HID_STATE_CONNECTED,
+       BT_HID_STATE_DISCONNECTING,
+} bt_hid_state_t;
+
+typedef struct {
+       guint32 fd;
+       char remote_addr[BT_ADDRESS_STRING_SIZE];
+       char *object_path;
+       GDBusMethodInvocation *context;
+       char *path;
+} bt_hid_agent_info_t;
+
+typedef struct {
+       unsigned char b[6];
+} __attribute__((packed)) bdaddr_t;
+
+/* Remote socket address */
+struct sockaddr_remote {
+       sa_family_t     family;
+       bdaddr_t        remote_bdaddr;
+       uint8_t         channel;
+};
+
+void _bt_hid_set_profile_state(bt_hid_state_t new_state);
+bt_hid_agent_error_t _bt_hid_register_application(gboolean register_flag,
+               const char *sender_name);
+
+bt_hid_agent_error_t __bt_hid_disconnect_profile(void);
+
+#endif /* __DEF_BT_HID_AGENT_H_ */
diff --git a/hid-agent/bluetooth-hid-manager.c b/hid-agent/bluetooth-hid-manager.c
new file mode 100644 (file)
index 0000000..f101b32
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "bluetooth-hid-agent.h"
+
+static char *sender;
+static bt_hid_state_t hid_state;
+
+static const char *__bt_hid_state2str(bt_hid_state_t state)
+{
+       switch (state) {
+       case BT_HID_STATE_UNAVAILABLE:
+               return "UNAVAILABLE";
+       case BT_HID_STATE_DISCONNECTED:
+               return "DISCONNECTED";
+       case BT_HID_STATE_CONNECTING:
+               return "CONNECTING";
+       case BT_HID_STATE_CONNECTED:
+               return "CONNECTED";
+       case BT_HID_STATE_DISCONNECTING:
+               return "DISCONNECTING";
+       }
+
+       return NULL;
+}
+
+#if 0
+static gboolean __bt_hid_check_for_callpath(const char *call_sender)
+{
+       GSList *s_list = app_list;
+       char *sender;
+
+       DBG_SECURE("sender is  = %s\n", call_sender);
+
+       if (call_sender == NULL) {
+
+               ERR("Invalid Parameters");
+               return FALSE;
+       }
+
+       /*check if the call is already registered*/
+       DBG("Checking if the call is already registered");
+       while (s_list != NULL) {
+               sender = s_list->data;
+
+               if (sender == NULL)
+                       break;
+
+               if (g_strcmp0(sender, call_sender) == 0) {
+                       DBG("sender path and call path match... so return true");
+                       return TRUE;
+               }
+
+               s_list = s_list->next;
+       }
+
+       ERR("Sender [%s] is not registered", call_sender);
+       return FALSE;
+}
+
+bt_hid_agent_error_t _bt_hid_register_application(gboolean register_flag,
+               const char *sender)
+{
+       char *sender_name;
+
+       if (sender == NULL)
+               return BT_HID_AGENT_ERROR_INVALID_PARAM;
+
+       DBG(" Requesting [%s]", register_flag ? "Register" : "Unregister");
+       DBG(" sender = %s", sender);
+
+       if (register_flag) {
+               if (__bt_hid_check_for_callpath(sender))
+                       return BT_HID_AGENT_ERROR_ALREADY_EXIST;
+
+               /* add call path to the senders list*/
+               sender_name = g_strdup(sender);
+               app_list = g_slist_append(app_list, sender_name);
+
+               return BT_HID_AGENT_ERROR_NONE;
+       } else {
+               /*remove the call from senders list */
+               GSList *s_list = app_list;
+
+               while (s_list != NULL) {
+                       sender_name = s_list->data;
+
+                       if (sender_name == NULL)
+                               return BT_HID_AGENT_ERROR_NOT_AVAILABLE;
+
+                       if (g_strcmp0(sender_name, sender) == 0) {
+                               app_list = g_slist_remove(app_list, sender_name);
+                               g_free(sender_name);
+
+                               if (app_list == NULL && hid_state == BT_HID_STATE_CONNECTED)
+                                       __bt_hid_disconnect_profile();
+
+                               return BT_HID_AGENT_ERROR_NONE;
+                       }
+                       s_list = s_list->next;
+               }
+
+               return BT_HID_AGENT_ERROR_NOT_AVAILABLE;
+       }
+}
+
+#else
+bt_hid_agent_error_t _bt_hid_register_application(gboolean register_flag,
+               const char *sender_name)
+{
+       if (sender_name == NULL)
+               return BT_HID_AGENT_ERROR_INVALID_PARAM;
+
+       if (register_flag == TRUE) {
+               if (sender != NULL) {
+                       ERR("Already registered [sender:%s]", sender_name);
+                       return BT_HID_AGENT_ERROR_ALREADY_EXIST;
+               }
+
+               /* set the call path to the sender */
+               sender = g_strdup(sender_name);
+               INFO("Registered [sender:%s]", sender_name);
+       } else {
+               if (sender == NULL)
+                       return BT_HID_AGENT_ERROR_NOT_AVAILABLE;
+               if (strcasecmp(sender, sender_name) != 0)
+                       return BT_HID_AGENT_ERROR_NOT_AVAILABLE;
+
+               /* unset the call path to the sender */
+               g_free(sender);
+               sender = NULL;
+               if (hid_state == BT_HID_STATE_CONNECTED)
+                       __bt_hid_disconnect_profile();
+               INFO("Unregistered [sender:%s]", sender_name);
+       }
+
+       return BT_HID_AGENT_ERROR_NONE;
+}
+#endif
+
+void _bt_hid_set_profile_state(bt_hid_state_t new_state)
+{
+       INFO_C("state changed [%s] =>[%s]", __bt_hid_state2str(hid_state),
+                                       __bt_hid_state2str(new_state));
+       hid_state = new_state;
+
+       switch (new_state) {
+       case BT_HID_STATE_UNAVAILABLE:
+       case BT_HID_STATE_DISCONNECTED:
+               break;
+       case BT_HID_STATE_CONNECTED:
+               break;
+       case BT_HID_STATE_CONNECTING:
+       case BT_HID_STATE_DISCONNECTING:
+       default:
+               break;
+       }
+}
+
+bt_hid_state_t _bt_hid_get_profile_state(void)
+{
+       return hid_state;
+}
diff --git a/hid-agent/org.bluez.hid_agent.service b/hid-agent/org.bluez.hid_agent.service
new file mode 100644 (file)
index 0000000..e678c59
--- /dev/null
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=org.bluez.hid_agent
+Exec=/usr/bin/bluetooth-hid-agent
+User=network_fw
+Group=network_fw