diff options
author | Wim Taymans <wtaymans@redhat.com> | 2014-08-08 21:06:05 +0200 |
---|---|---|
committer | Wim Taymans <wtaymans@redhat.com> | 2014-08-08 21:06:05 +0200 |
commit | 58c986ac9d2a2bce49958b82d8018d6eb2bb0f0a (patch) | |
tree | 7c8796491e439449c245a5d4e7e09164e96ab013 | |
parent | 021f1283950cae691d2c0506da162b09e6815b29 (diff) |
-rwxr-xr-x | compile | 2 | ||||
-rw-r--r-- | hsp-daemon.c | 785 | ||||
-rw-r--r-- | make-agent.c | 299 |
3 files changed, 1086 insertions, 0 deletions
@@ -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; +} |