summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wtaymans@redhat.com>2014-08-08 21:06:05 +0200
committerWim Taymans <wtaymans@redhat.com>2014-08-08 21:06:05 +0200
commit58c986ac9d2a2bce49958b82d8018d6eb2bb0f0a (patch)
tree7c8796491e439449c245a5d4e7e09164e96ab013
parent021f1283950cae691d2c0506da162b09e6815b29 (diff)
More test codeHEADmaster
-rwxr-xr-xcompile2
-rw-r--r--hsp-daemon.c785
-rw-r--r--make-agent.c299
3 files changed, 1086 insertions, 0 deletions
diff --git a/compile b/compile
index 81804ed..0460ee9 100755
--- a/compile
+++ b/compile
@@ -1 +1,3 @@
gcc `pkg-config --cflags --libs gio-unix-2.0` -o make-hsp-ag make-hsp-ag.c
+gcc `pkg-config --cflags --libs gio-unix-2.0` -o make-agent make-agent.c
+gcc `pkg-config --cflags --libs gio-unix-2.0` -o hsp-daemon hsp-daemon.c
diff --git a/hsp-daemon.c b/hsp-daemon.c
new file mode 100644
index 0000000..7f01b72
--- /dev/null
+++ b/hsp-daemon.c
@@ -0,0 +1,785 @@
+/*
+ * Copyright (C) 2014 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sco.h>
+
+
+#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GDBusNodeInfo *introspection_data = NULL;
+
+/* Introspection data for the service we are exporting */
+static const gchar introspection_xml[] =
+ "<node>"
+ " <interface name='org.bluez.Profile1'>"
+ " <method name='Release'>"
+ " </method>"
+ " <method name='Cancel'>"
+ " </method>"
+ " <method name='RequestDisconnection'>"
+ " <arg type='o' name='device' direction='in'/>"
+ " </method>"
+ " <method name='NewConnection'>"
+ " <arg type='o' name='device' direction='in'/>"
+ " <arg type='h' name='fd' direction='in'/>"
+ " <arg type='a{sv}' name='opts' direction='in'/>"
+ " </method>"
+ " </interface>"
+ " <interface name='org.bluez.MediaTransport1'>"
+ " <method name='Release'>"
+ " </method>"
+ " <method name='Acquire'>"
+ " <arg name='fd' direction='out' type='h'/>"
+ " <arg name='mtu_r' direction='out' type='q'/>"
+ " <arg name='mtu_w' direction='out' type='q'/>"
+ " </method>"
+ " <method name='TryAcquire'>"
+ " <arg name='fd' direction='out' type='h'/>"
+ " <arg name='mtu_r' direction='out' type='q'/>"
+ " <arg name='mtu_w' direction='out' type='q'/>"
+ " </method>"
+ " <property type='s' name='State' access='read'/>"
+ " </interface>"
+ "</node>";
+
+typedef struct {
+ GDBusConnection *conn;
+ GDBusProxy *manager;
+
+ gchar *uuid;
+ gchar *name;
+ guint id;
+
+ GHashTable *objs;
+} Profile;
+
+typedef struct {
+ Profile *profile;
+
+ gchar *obj;
+ gint fd;
+ guint id;
+ gchar *state;
+
+ gchar *owner;
+ gchar *spath;
+
+ gchar *src_addr;
+ gchar *dst_addr;
+
+ GVariantIter *props;
+ gchar *name;
+
+ GIOChannel *channel;
+} MediaTransport;
+
+static void send_set_configuration_reply (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
+
+ g_dbus_connection_call_finish (connection, res, &error);
+
+ if (error) {
+ g_printerr ("error doing SetConfiguration %s\n", error->message);
+ g_clear_error (&error);
+ }
+}
+
+static void send_set_configuration (MediaTransport *t)
+{
+ /* send transport to endpoint */
+ GVariantBuilder *b;
+
+ b = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (b, "{sv}", "UUID", g_variant_new_string ("0000111f-0000-1000-8000-00805f9b34fb"));
+ g_variant_builder_add (b, "{sv}", "Device", g_variant_new_object_path (t->obj));
+
+ g_dbus_connection_call (t->profile->conn,
+ t->owner,
+ t->spath,
+ "org.bluez.MediaEndpoint1",
+ "SetConfiguration",
+ g_variant_new ("(oa{sv})",
+ t->name,
+ b),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ send_set_configuration_reply,
+ t);
+}
+
+static void send_clear_configuration_reply (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
+
+ g_dbus_connection_call_finish (connection, res, &error);
+
+ if (error) {
+ g_printerr ("error doing ClearConfiguration %s\n", error->message);
+ g_clear_error (&error);
+ }
+}
+
+static void send_clear_configuration (MediaTransport *t)
+{
+ /* send transport to endpoint */
+ g_dbus_connection_call (t->profile->conn,
+ t->owner,
+ t->spath,
+ "org.bluez.MediaEndpoint1",
+ "ClearConfiguration",
+ g_variant_new ("(o)",
+ t->name),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ send_clear_configuration_reply,
+ t);
+}
+
+static void
+media_transport_configure_endpoint (MediaTransport *t)
+{
+ GVariantIter *props_i = t->props;
+ GVariant *value;
+ gchar *key;
+
+ /* now go over all registered endpoints and inform them about the
+ * new transport object */
+ while (g_variant_iter_next (props_i, "{&sv}", &key, &value)) {
+ g_print (" prop: %s\n", key);
+
+ if (g_str_equal (key, "MediaEndpoints")) {
+ GVariantIter *endpoints_i;
+ gchar *owner;
+ GVariant *oprops;
+
+ g_variant_get (value, "a{sv}", &endpoints_i);
+
+ while (g_variant_iter_next (endpoints_i, "{sv}", &owner, &oprops)) {
+ GVariantIter *oprops_i;
+ gchar *pname;
+ const gchar *spath;
+ GVariant *pval;
+
+ g_print (" owner: %s\n", owner);
+
+ g_variant_get (oprops, "a{sv}", &oprops_i);
+
+ while (g_variant_iter_next (oprops_i, "{&sv}", &pname, &pval)) {
+ if (g_variant_is_of_type (pval, G_VARIANT_TYPE_OBJECT_PATH)) {
+ t->spath = g_variant_dup_string (pval, NULL);
+ g_print (" key: %s object-path=%s\n", pname, spath);
+ } else if (g_variant_is_of_type (pval, G_VARIANT_TYPE_BYTE)) {
+ g_print (" key: %s byte=%c\n", pname, g_variant_get_byte (pval));
+ } else if (g_variant_is_of_type (pval, G_VARIANT_TYPE_ARRAY)) {
+ g_print (" key: %s array\n", pname);
+ } else if (g_variant_is_of_type (pval, G_VARIANT_TYPE_STRING)) {
+ g_print (" key: %s string=%s\n", pname, g_variant_get_string (pval, NULL));
+ } else {
+ g_print (" key: %s (%s)\n", pname, g_variant_get_type_string (pval));
+ }
+
+ g_variant_unref (pval);
+ }
+
+ t->owner = owner;
+
+ send_set_configuration (t);
+
+ g_variant_iter_free (oprops_i);
+ g_variant_unref (oprops);
+ }
+ g_variant_iter_free (endpoints_i);
+ }
+ g_variant_unref (value);
+ }
+ g_variant_iter_free (props_i);
+}
+
+static gboolean
+rfcomm_io_cb (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ gchar buf[512];
+ GIOStatus st;
+ MediaTransport *t = data;
+
+ g_print ("condition %d\n", condition);
+
+ if (condition & (G_IO_ERR | G_IO_HUP))
+ return FALSE;
+
+ if (condition & G_IO_IN) {
+ gint fd = g_io_channel_unix_get_fd (source);
+ gsize length;
+
+ g_print ("we can read\n", condition);
+
+ g_io_channel_read_chars (source, buf, 512, &length, NULL);
+ buf[length] = 0;
+ g_print ("%d %s\n", length, buf);
+
+ if (g_str_has_prefix (buf, "AT+BRSF=")) {
+ write (fd, "\r\n+BRSF:254\r\n", 13);
+ write (fd, "\r\nOK\r\n", 5);
+ }
+ else if (g_str_has_prefix (buf, "AT+CIND=?")) {
+ write (fd, "\r\n+CIND:254\r\n", 13);
+ write (fd, "\r\nOK\r\n", 5);
+ }
+ else if (g_str_has_prefix (buf, "AT+CIND?")) {
+ write (fd, "\r\n+CIND:254\r\n", 13);
+ write (fd, "\r\nOK\r\n", 5);
+ }
+ else if (g_str_has_prefix (buf, "AT+CMER=")) {
+ write (fd, "\r\n+VGM:15\r\n", 11);
+ write (fd, "\r\n+VGS:15\r\n", 11);
+ write (fd, "\r\nOK\r\n", 5);
+ }
+ else if (g_str_has_prefix (buf, "AT+CMEE=")) {
+ write (fd, "\r\nOK\r\n", 5);
+ media_transport_configure_endpoint (t);
+ }
+ else {
+ write (fd, "\r\nOK\r\n", 5);
+ }
+ }
+ return TRUE;
+}
+
+static void
+transport_set_state (MediaTransport *t, const gchar *state)
+{
+ GVariantBuilder *builder;
+ GVariantBuilder *invalidated_builder;
+ GError *error;
+
+ error = NULL;
+ builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+ invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+ g_variant_builder_add (builder,
+ "{sv}",
+ "State",
+ g_variant_new_string (state));
+ g_dbus_connection_emit_signal (t->profile->conn,
+ NULL,
+ t->name,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ g_variant_new ("(sa{sv}as)",
+ "org.bluez.MediaTransport1",
+ builder,
+ invalidated_builder),
+ &error);
+ g_assert_no_error (error);
+}
+
+static void
+transport_acquire (MediaTransport *t,
+ gboolean optional,
+ GDBusConnection *connection,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ GUnixFDList *fdlist;
+ GDBusMessage *reply;
+ GError *error;
+ guchar *blob;
+ gsize out_size;
+ struct sockaddr_sco addr;
+ int err, i;
+ GIOCondition cond;
+ GIOChannel *io;
+ bdaddr_t src;
+ bdaddr_t dst;
+ int voice = 0x60;
+ socklen_t len;
+ gchar *src_addr;
+ gchar *dst_addr;
+
+ src_addr = t->src_addr;
+ dst_addr = t->dst_addr;
+
+ for (i = 5; i >= 0; i--, src_addr += 3)
+ src.b[i] = strtol(src_addr, NULL, 16);
+ for (i = 5; i >= 0; i--, dst_addr += 3)
+ dst.b[i] = strtol(dst_addr, NULL, 16);
+
+ t->fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ if (t->fd < 0) {
+ g_printerr("socket(SEQPACKET, SCO)");
+ return;
+ }
+
+ g_print ("got fd %d\n", t->fd);
+ transport_set_state (t, "pending");
+
+ fdlist = g_unix_fd_list_new ();
+ g_unix_fd_list_append (fdlist, t->fd, NULL);
+ g_dbus_method_invocation_return_value_with_unix_fd_list (
+ invocation, g_variant_new ("(hqq)", 0, 48, 48), fdlist);
+
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, &src);
+
+ if (bind(t->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind()");
+ return;
+ }
+
+ if (voice) {
+ struct bt_voice opts;
+
+ /* SCO voice setting */
+ memset(&opts, 0, sizeof(opts));
+ opts.setting = voice;
+ if (setsockopt(t->fd, SOL_BLUETOOTH, BT_VOICE, &opts, sizeof(opts)) < 0) {
+ perror("setsockopt()");
+ return;
+ }
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, &dst);
+
+ g_printerr ("doing connect\n");
+ err = connect(t->fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
+ perror("connect()");
+ return;
+ }
+
+ g_printerr ("connected\n");
+
+ transport_set_state (t, "active");
+}
+
+static void
+transport_release (MediaTransport *t,
+ GDBusConnection *connection,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+
+ g_print ("close fd %d\n", t->fd);
+
+ if (t->fd != -1) {
+ shutdown (t->fd, SHUT_RDWR);
+ close (t->fd);
+ }
+ t->fd = -1;
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ transport_set_state (t, "idle");
+}
+
+static void
+transport_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ MediaTransport *t = user_data;
+
+ if (g_strcmp0 (method_name, "Acquire") == 0) {
+ transport_acquire (t, TRUE, connection, parameters, invocation);
+ } else if (g_strcmp0 (method_name, "TryAcquire") == 0) {
+ transport_acquire (t, FALSE, connection, parameters, invocation);
+ } else if (g_strcmp0 (method_name, "Release") == 0) {
+ transport_release (t, connection, parameters, invocation);
+ }
+}
+
+static GVariant *
+transport_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ MediaTransport *t = user_data;
+ GVariant *ret;
+
+ ret = NULL;
+ if (g_strcmp0 (property_name, "State") == 0) {
+ ret = g_variant_new_string (t->state);
+ }
+ return ret;
+}
+
+static const GDBusInterfaceVTable transport_interface_vtable =
+{
+ transport_method_call,
+ transport_get_property,
+ NULL
+};
+
+static MediaTransport *
+media_transport_free (MediaTransport *t)
+{
+ g_print ("media transport free %p\n", t);
+
+ send_clear_configuration (t);
+
+ if (t->id)
+ g_dbus_connection_unregister_object (t->profile->conn, t->id);
+ if (t->channel)
+ g_io_channel_unref (t->channel);
+ g_free (t->owner);
+ g_free (t->spath);
+ g_free (t->src_addr);
+ g_free (t->dst_addr);
+ g_free (t->obj);
+ g_free (t);
+}
+
+static MediaTransport *
+media_transport_new (Profile *p, const gchar *obj, gint fd, GVariantIter *props)
+{
+ MediaTransport *t;
+ GError *error = NULL;
+ GVariant *res, *var;
+ const gchar *adapter;
+
+ t = g_new0 (MediaTransport, 1);
+ t->profile = p;
+ t->obj = g_strdup (obj);
+ t->fd = fd;
+ t->props = props;
+ t->state = "idle";
+
+ g_print ("new media transport %p\n", t);
+
+ /* make a new transport object to handle the setup of the SCO connection */
+ t->name = g_strdup_printf ("%s/fd%d", obj, fd);
+ t->id = g_dbus_connection_register_object (p->conn,
+ t->name,
+ introspection_data->interfaces[1],
+ &transport_interface_vtable,
+ t,
+ NULL, /* user_data_free_func */
+ &error); /* GError** */
+ if (t->id == 0) {
+ g_printerr ("error registering object %s\n", error->message);
+ media_transport_free (t);
+ return NULL;
+ }
+
+ res = g_dbus_connection_call_sync (p->conn,
+ "org.bluez",
+ obj,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.bluez.Device1",
+ "Address"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (res == NULL) {
+ g_printerr ("Can't get Device Address", error->message);
+ return NULL;
+ }
+ g_variant_get (res, "(v)", &var);
+ t->dst_addr = g_variant_dup_string (var, NULL);
+ g_variant_unref (var);
+ g_print ("device addres %s\n", t->dst_addr);
+
+ res = g_dbus_connection_call_sync (p->conn,
+ "org.bluez",
+ obj,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.bluez.Device1",
+ "Adapter"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (res == NULL) {
+ g_printerr ("Can't get Adapter", error->message);
+ return NULL;
+ }
+ g_variant_get (res, "(v)", &var);
+ adapter = g_variant_get_string (var, NULL);
+ g_print ("device adapter %s\n", adapter);
+
+ res = g_dbus_connection_call_sync (p->conn,
+ "org.bluez",
+ adapter,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.bluez.Adapter1",
+ "Address"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ g_variant_unref (var);
+
+ if (res == NULL) {
+ g_printerr ("Can't get Adapter Address", error->message);
+ return NULL;
+ }
+ g_variant_get (res, "(v)", &var);
+ t->src_addr = g_variant_dup_string (var, NULL);
+ g_variant_unref (var);
+ g_print ("adapter addres %s\n", t->src_addr);
+
+ t->channel = g_io_channel_unix_new (fd);
+ g_io_channel_set_close_on_unref (t->channel, TRUE);
+ g_io_add_watch (t->channel, G_IO_IN | G_IO_ERR | G_IO_HUP, rfcomm_io_cb, t);
+
+ g_hash_table_insert (p->objs, (gpointer) obj, t);
+
+ return t;
+}
+
+static void
+profile_new_connection (Profile *p,
+ GDBusConnection *connection,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ gchar *obj;
+ gint32 fd;
+ GVariantIter *props;
+ GDBusMessage *message;
+ GUnixFDList * fdlist;
+ MediaTransport *t;
+
+ g_variant_get (parameters, "(oha{sv})", &obj, &fd, &props);
+
+ message = g_dbus_method_invocation_get_message (invocation);
+ fdlist = g_dbus_message_get_unix_fd_list (message);
+ fd = g_unix_fd_list_get (fdlist, fd, NULL);
+
+ g_print ("NewConnection %s %d\n", obj, fd);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ /* make a new transport object to handle the setup of the SCO connection */
+ t = media_transport_new (p, obj, fd, props);
+}
+
+static void
+profile_request_disconnection (Profile *p,
+ GDBusConnection *connection,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ gchar *obj;
+ MediaTransport *t;
+
+ g_variant_get (parameters, "(o)", &obj);
+ g_print ("RequestDisconnection %s\n", obj);
+
+ t = g_hash_table_lookup (p->objs, obj);
+ if (t == NULL) {
+ g_warning ("unknown transport object %s", obj);
+ goto done;
+ }
+
+ g_hash_table_remove (p->objs, obj);
+ media_transport_free (t);
+
+done:
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void
+profile_release (Profile *p,
+ GDBusConnection *connection,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void
+profile_cancel (Profile *p,
+ GDBusConnection *connection,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void
+profile_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ Profile *p = user_data;
+
+ if (g_strcmp0 (method_name, "Release") == 0) {
+ profile_release (p, connection, parameters, invocation);
+ } else if (g_strcmp0 (method_name, "Cancel") == 0) {
+ profile_cancel (p, connection, parameters, invocation);
+ } else if (g_strcmp0 (method_name, "RequestDisconnection") == 0) {
+ profile_request_disconnection (p, connection, parameters, invocation);
+ } else if (g_strcmp0 (method_name, "NewConnection") == 0) {
+ profile_new_connection (p, connection, parameters, invocation);
+ } else
+ g_print ("Unhandled %s\n", method_name);
+}
+
+static const GDBusInterfaceVTable profile_interface_vtable =
+{
+ profile_method_call,
+ NULL,
+ NULL
+};
+
+static void profile_free (Profile *p)
+{
+ if (p->id)
+ g_dbus_connection_unregister_object (p->conn, p->id);
+ g_free (p->uuid);
+ g_free (p->name);
+ g_hash_table_unref (p->objs);
+ g_free (p);
+}
+
+static Profile *
+register_profile (GDBusConnection *connection, GDBusProxy * manager, const gchar *uuid)
+{
+ GError *error = NULL;
+ guint registration_id;
+ Profile *profile;
+ GVariant *res;
+
+ profile = g_new0 (Profile, 1);
+ profile->conn = connection;
+ profile->manager = manager;
+ profile->name = g_strdup_printf ("/org/test/Profile");
+ profile->uuid = g_strdup (uuid);
+ profile->objs = g_hash_table_new (g_str_hash, g_str_equal);
+
+ profile->id = g_dbus_connection_register_object (connection,
+ profile->name,
+ introspection_data->interfaces[0],
+ &profile_interface_vtable,
+ profile,
+ NULL, /* user_data_free_func */
+ &error); /* GError** */
+ if (profile->id == 0) {
+ g_printerr ("error registering object %s\n", error->message);
+ profile_free (profile);
+ return NULL;
+ }
+
+ g_print ("Register profile\n");
+ res = g_dbus_proxy_call_sync (manager,
+ "RegisterProfile",
+ g_variant_new ("(osa{sv})",
+ profile->name,
+ uuid, NULL),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (res == NULL) {
+ g_printerr ("error registering %s\n", error->message);
+ profile_free (profile);
+ return NULL;
+ }
+ g_print ("Profile registered\n");
+ g_variant_unref (res);
+
+ return profile;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GDBusConnection * connection;
+ GError *error = NULL;
+ GMainLoop *loop;
+ guint registration_id;
+ GDBusProxy *manager;
+
+ g_print ("connecting to system bus\n");
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (error != NULL) {
+ g_printerr ("error getting bus: %s", error->message);
+ return -1;
+ }
+
+ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_assert (introspection_data != NULL);
+
+ g_print ("making proxy for ProfileManager1\n");
+ manager = g_dbus_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL, "org.bluez", "/org/bluez", "org.bluez.ProfileManager1",
+ NULL, &error);
+ if (error != NULL) {
+ g_printerr ("error getting ProfileManager1: %s", error->message);
+ return -1;
+ }
+
+ if (register_profile (connection, manager, HFP_AG_UUID) == NULL)
+ return -1;
+
+ g_print ("going into mainloop\n");
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+ g_print ("exit mainloop\n");
+
+ g_dbus_node_info_unref (introspection_data);
+
+ return 0;
+}
diff --git a/make-agent.c b/make-agent.c
new file mode 100644
index 0000000..e70d6cc
--- /dev/null
+++ b/make-agent.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2014 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sco.h>
+
+
+#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GDBusNodeInfo *introspection_data = NULL;
+
+/* Introspection data for the service we are exporting */
+static const gchar introspection_xml[] =
+ "<node>"
+ " <interface name='org.ofono.HandsfreeAudioAgent'>"
+ " <method name='Release'>"
+ " </method>"
+ " <method name='NewConnection'>"
+ " <arg type='o' name='device' direction='in'/>"
+ " <arg type='h' name='fd' direction='in'/>"
+ " <arg type='y' name='codec' direction='in'/>"
+ " </method>"
+ " </interface>"
+ "</node>";
+
+typedef struct {
+ GDBusConnection *conn;
+ GDBusProxy *manager;
+
+ gchar *name;
+ guint id;
+
+ GHashTable *objs;
+} Agent;
+
+typedef struct {
+ Agent *agent;
+
+ gchar *obj;
+ gint fd;
+
+ GIOChannel *channel;
+} Connection;
+
+static Connection *
+connection_free (Connection *t)
+{
+ g_print ("connection free %p\n", t);
+
+ if (t->channel)
+ g_io_channel_unref (t->channel);
+ g_free (t->obj);
+ g_free (t);
+}
+
+static gboolean
+sco_io_cb (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ gchar buf[1024];
+ gsize length;
+ gint fd = g_io_channel_unix_get_fd (source);
+
+ if (condition & G_IO_IN) {
+ length = recv (fd, buf, 1024, 0);
+ if (length <= 0)
+ perror ("recv()");
+ g_print ("-");
+ } else if (condition & G_IO_OUT) {
+ gint i;
+
+ for (i = 0; i < 48; i++)
+ buf[i] = g_random_int_range (0, 255);
+
+ length = send (fd, buf, 48, 0);
+ if (length != 48)
+ perror ("send()");
+
+ g_print ("+");
+ usleep(2500);
+ } else if (condition & (G_IO_ERR | G_IO_HUP)) {
+ g_print ("got error\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static Connection *
+connection_new (Agent *a, const gchar *obj, gint fd)
+{
+ Connection *c;
+ GError *error = NULL;
+ GVariant *res, *var;
+ const gchar *adapter;
+
+ c = g_new0 (Connection, 1);
+ c->agent = a;
+ c->obj = g_strdup (obj);
+ c->fd = fd;
+
+ g_print ("new connection %p\n", c);
+
+ c->channel = g_io_channel_unix_new (fd);
+ g_io_channel_set_close_on_unref (c->channel, TRUE);
+ g_io_add_watch (c->channel, G_IO_IN | G_IO_ERR | G_IO_HUP, sco_io_cb, c);
+
+ g_hash_table_insert (a->objs, (gpointer) obj, c);
+
+ return c;
+}
+
+static void
+agent_new_connection (Agent *a,
+ GDBusConnection *connection,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ gchar *obj;
+ gint32 fd;
+ guint codec;
+ GDBusMessage *message;
+ GUnixFDList * fdlist;
+
+ g_variant_get (parameters, "(ohy)", &obj, &fd, &codec);
+
+ message = g_dbus_method_invocation_get_message (invocation);
+ fdlist = g_dbus_message_get_unix_fd_list (message);
+ fd = g_unix_fd_list_get (fdlist, fd, NULL);
+
+ g_print ("NewConnection %s %d %d\n", obj, fd, codec);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ connection_new (a, obj, fd);
+}
+
+static void
+agent_release (Agent *a,
+ GDBusConnection *connection,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation)
+{
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void
+agent_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ Agent *a = user_data;
+
+ if (g_strcmp0 (method_name, "Release") == 0) {
+ agent_release (a, connection, parameters, invocation);
+ } else if (g_strcmp0 (method_name, "NewConnection") == 0) {
+ agent_new_connection (a, connection, parameters, invocation);
+ } else
+ g_print ("Unhandled %s\n", method_name);
+}
+
+static const GDBusInterfaceVTable agent_interface_vtable =
+{
+ agent_method_call,
+ NULL,
+ NULL
+};
+
+static void agent_free (Agent *a)
+{
+ if (a->id)
+ g_dbus_connection_unregister_object (a->conn, a->id);
+ g_free (a->name);
+ g_hash_table_unref (a->objs);
+ g_free (a);
+}
+
+static Agent *
+register_agent (GDBusConnection *connection, GDBusProxy * manager)
+{
+ GError *error = NULL;
+ guint registration_id;
+ Agent *agent;
+ GVariant *res;
+ GVariantBuilder *b;
+
+ agent = g_new0 (Agent, 1);
+ agent->conn = connection;
+ agent->manager = manager;
+ agent->name = g_strdup_printf ("/org/test/HandsfreeAgent");
+ agent->objs = g_hash_table_new (g_str_hash, g_str_equal);
+
+ agent->id = g_dbus_connection_register_object (connection,
+ agent->name,
+ introspection_data->interfaces[0],
+ &agent_interface_vtable,
+ agent,
+ NULL, /* user_data_free_func */
+ &error); /* GError** */
+ if (agent->id == 0) {
+ g_printerr ("error registering agent %s\n", error->message);
+ agent_free (agent);
+ return NULL;
+ }
+
+ g_print ("Register agent\n");
+
+ b = g_variant_builder_new (G_VARIANT_TYPE ("ay"));
+ g_variant_builder_add (b, "y", 1);
+
+ res = g_dbus_proxy_call_sync (manager,
+ "Register",
+ g_variant_new ("(oay)",
+ agent->name,
+ b, NULL),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (res == NULL) {
+ g_printerr ("error registering %s\n", error->message);
+ agent_free (agent);
+ return NULL;
+ }
+ g_print ("Agent registered\n");
+ g_variant_unref (res);
+
+ return agent;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GDBusConnection * connection;
+ GError *error = NULL;
+ GMainLoop *loop;
+ guint registration_id;
+ GDBusProxy *manager;
+
+ g_print ("connecting to system bus\n");
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (error != NULL) {
+ g_printerr ("error getting bus: %s", error->message);
+ return -1;
+ }
+
+ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_assert (introspection_data != NULL);
+
+ g_print ("making proxy for HandsfreeAudioManager\n");
+ manager = g_dbus_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL, "org.ofono", "/", "org.ofono.HandsfreeAudioManager",
+ NULL, &error);
+ if (error != NULL) {
+ g_printerr ("error getting HandsfreeAudioManager: %s", error->message);
+ return -1;
+ }
+
+ if (register_agent (connection, manager) == NULL)
+ return -1;
+
+ g_print ("going into mainloop\n");
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+ g_print ("exit mainloop\n");
+
+ g_dbus_node_info_unref (introspection_data);
+
+ return 0;
+}