--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */