aboutsummaryrefslogtreecommitdiffstats
path: root/binding/bluetooth-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'binding/bluetooth-agent.c')
-rw-r--r--binding/bluetooth-agent.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/binding/bluetooth-agent.c b/binding/bluetooth-agent.c
new file mode 100644
index 0000000..b6ae5fe
--- /dev/null
+++ b/binding/bluetooth-agent.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2018 Konsulko Group
+ * Author: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <glib.h>
+#include <stdlib.h>
+#include <gio/gio.h>
+#include <glib-object.h>
+
+#include <json-c/json.h>
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+#include "bluetooth-api.h"
+#include "bluetooth-common.h"
+
+/* Introspection data for the agent service */
+static const gchar introspection_xml[] =
+"<node>"
+" <interface name='org.bluez.Agent1'>"
+" <method name='RequestConfirmation'>"
+" <arg name='device' direction='in' type='o'/>"
+" <arg name='passkey' direction='in' type='u'/>"
+" </method>"
+" <method name='AuthorizeService'>"
+" <arg name='device' direction='in' type='o'/>"
+" <arg name='uuid' direction='in' type='s'/>"
+" </method>"
+" <method name='Cancel'>"
+" </method>"
+" </interface>"
+"</node>";
+
+static void handle_method_call(
+ GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ struct bluetooth_state *ns = user_data;
+ struct call_work *cw;
+ GError *error = NULL;
+ json_object *jev = NULL;
+ const gchar *path = NULL;
+
+ /* AFB_INFO("sender=%s", sender_name);
+ AFB_INFO("object_path=%s", object_path);
+ AFB_INFO("interface=%s", interface_name);
+ AFB_INFO("method=%s", method_name); */
+
+ if (!g_strcmp0(method_name, "RequestConfirmation")) {
+ int pin;
+
+ g_variant_get(parameters, "(ou)", &path, &pin);
+
+ call_work_lock(ns);
+
+ /* can only occur while a pair is issued */
+ cw = call_work_lookup_unlocked(ns, "device", NULL, "RequestConfirmation");
+
+ /* if nothing is pending return an error */
+ /* TODO: allow client side pairing */
+ if (!cw)
+ cw = call_work_create_unlocked(ns, "device", NULL,
+ "RequestConfirmation", NULL, &error);
+
+ if (!cw) {
+ call_work_unlock(ns);
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ "org.bluez.Error.Rejected",
+ "No connection pending");
+ return;
+ }
+
+ jev = json_object_new_object();
+ json_object_object_add(jev, "action", json_object_new_string("request_confirmation"));
+ json_object_object_add(jev, "device", json_object_new_string(path));
+ json_object_object_add(jev, "pincode", json_object_new_int(pin));
+
+ cw->agent_data.pin_code = pin;
+ cw->agent_data.device_path = g_strdup(path);
+ cw->invocation = invocation;
+
+ call_work_unlock(ns);
+
+ afb_event_push(ns->agent_event, jev);
+
+ return;
+ } else if (!g_strcmp0(method_name, "AuthorizeService")) {
+
+ /* g_variant_get(parameters, "(os)", &path, &service);
+
+ jev = json_object_new_object();
+ json_object_object_add(jev, "action", json_object_new_string("authorize_service"));
+ json_object_object_add(jev, "path", json_object_new_string(path));
+ json_object_object_add(jev, "uuid", json_object_new_string(service));
+
+ afb_event_push(ns->agent_event, jev); */
+
+ return g_dbus_method_invocation_return_value(invocation, NULL);
+ } else if (!g_strcmp0(method_name, "Cancel")) {
+
+ call_work_lock(ns);
+
+ /* can only occur while a pair is issued */
+ cw = call_work_lookup_unlocked(ns, "device", NULL, "RequestConfirmation");
+
+ if (!cw) {
+ call_work_unlock(ns);
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ "org.bluez.Error.Rejected",
+ "No connection pending");
+ return;
+ }
+
+ jev = json_object_new_object();
+ json_object_object_add(jev, "action", json_object_new_string("canceled_pairing"));
+
+ afb_event_push(ns->agent_event, jev);
+
+ call_work_destroy_unlocked(cw);
+ call_work_unlock(ns);
+
+ return g_dbus_method_invocation_return_value(invocation, NULL);
+ }
+
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ "org.freedesktop.DBus.Error.UnknownMethod",
+ "Uknown method");
+}
+
+static const GDBusInterfaceVTable interface_vtable = {
+ .method_call = handle_method_call,
+ .get_property = NULL,
+ .set_property = NULL,
+};
+
+static void on_bus_acquired(GDBusConnection *connection,
+ const gchar *name, gpointer user_data)
+{
+ struct init_data *id = user_data;
+ struct bluetooth_state *ns = id->ns;
+ GVariant *result;
+ GError *error = NULL;
+
+ AFB_INFO("agent bus acquired - registering %s", ns->agent_path);
+
+ ns->registration_id = g_dbus_connection_register_object(connection,
+ ns->agent_path,
+ ns->introspection_data->interfaces[0],
+ &interface_vtable,
+ ns, /* user data */
+ NULL, /* user_data_free_func */
+ NULL);
+
+ if (!ns->registration_id) {
+ AFB_ERROR("failed to register agent to dbus");
+ goto err_unable_to_register_bus;
+
+ }
+
+ result = agentmanager_call(ns, "RegisterAgent",
+ g_variant_new("(os)", ns->agent_path, "KeyboardDisplay"),
+ &error);
+ if (!result) {
+ AFB_ERROR("failed to register agent to bluez");
+ goto err_unable_to_register_bluez;
+ }
+ g_variant_unref(result);
+
+ result = agentmanager_call(ns, "RequestDefaultAgent",
+ g_variant_new("(o)", ns->agent_path),
+ &error);
+ if (!result) {
+ AFB_ERROR("failed to request default agent to bluez");
+ goto err_unable_to_request_default_agent_bluez;
+ }
+ g_variant_unref(result);
+
+ ns->agent_registered = TRUE;
+
+ AFB_INFO("agent registered at %s", ns->agent_path);
+ signal_init_done(id, 0);
+
+ return;
+
+err_unable_to_request_default_agent_bluez:
+ agentmanager_call(ns, "UnregisterAgent",
+ g_variant_new("(o)", ns->agent_path),
+ &error);
+
+err_unable_to_register_bluez:
+ g_dbus_connection_unregister_object(ns->conn, ns->registration_id);
+ ns->registration_id = 0;
+
+err_unable_to_register_bus:
+ signal_init_done(id, -1);
+}
+
+int bluetooth_register_agent(struct init_data *id)
+{
+ struct bluetooth_state *ns = id->ns;
+
+ ns->agent_path = g_strdup_printf("%s/agent%d",
+ BLUEZ_PATH, getpid());
+ if (!ns->agent_path) {
+ AFB_ERROR("can't create agent path");
+ goto out_no_agent_path;
+ }
+
+ ns->introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
+ if (!ns->introspection_data) {
+ AFB_ERROR("can't create introspection data");
+ goto out_no_introspection_data;
+ }
+
+ ns->agent_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, BLUEZ_AGENT_INTERFACE,
+ G_BUS_NAME_OWNER_FLAGS_REPLACE |
+ G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
+ on_bus_acquired,
+ NULL,
+ NULL,
+ id,
+ NULL);
+ if (!ns->agent_id) {
+ AFB_ERROR("can't create agent bus instance");
+ goto out_no_bus_name;
+ }
+
+ return 0;
+
+out_no_bus_name:
+ g_dbus_node_info_unref(ns->introspection_data);
+out_no_introspection_data:
+ g_free(ns->agent_path);
+out_no_agent_path:
+ return -1;
+}
+
+void bluetooth_unregister_agent(struct bluetooth_state *ns)
+{
+ g_bus_unown_name(ns->agent_id);
+ g_dbus_node_info_unref(ns->introspection_data);
+ g_free(ns->agent_path);
+}