Move HandsFree HF RFComm server from audio/manager.c to audio/telephony.c. Update the HandsfreeGateway API to reflect this change (remove agent related methods). Doing this, RFComm server related to HandsfreeGateway is only started when an agent registers for this role of HandsFree Profile. --- audio/device.h | 1 + audio/gateway.c | 392 +++++++++++++---------------------------------------- audio/gateway.h | 8 + audio/manager.c | 136 +------------------ audio/media.c | 9 +- audio/telephony.c | 275 +++++++++++++++++++++++++++++++++++++ audio/transport.c | 4 + doc/hfp-api.txt | 48 ------- 8 files changed, 393 insertions(+), 480 deletions(-) diff --git a/audio/device.h b/audio/device.h index 5117fca..f684f44 100644 --- a/audio/device.h +++ b/audio/device.h @@ -64,6 +64,7 @@ struct audio_device { struct target *target; guint hs_preauth_id; + guint gw_preauth_id; struct dev_priv *priv; }; diff --git a/audio/gateway.c b/audio/gateway.c index 9b1aab3..fa38447 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -46,21 +46,18 @@ #include "sdp-client.h" #include "device.h" #include "gateway.h" +#include "telephony.h" #include "log.h" #include "error.h" #include "btio.h" #include "dbus-common.h" +#include "../src/adapter.h" +#include "../src/device.h" #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif -struct hf_agent { - char *name; /* Bus id */ - char *path; /* D-Bus path */ - guint watch; /* Disconnect watch */ -}; - struct connect_cb { unsigned int id; gateway_stream_cb_t cb; @@ -71,12 +68,12 @@ struct gateway { gateway_state_t state; GIOChannel *rfcomm; GIOChannel *sco; - GIOChannel *incoming; + const char *connecting_uuid; + const char *connecting_path; GSList *callbacks; - struct hf_agent *agent; DBusMessage *msg; - int version; gateway_lock_t lock; + void *slc; }; struct gateway_state_callback { @@ -110,16 +107,6 @@ static const char *state2str(gateway_state_t state) } } -static void agent_free(struct hf_agent *agent) -{ - if (!agent) - return; - - g_free(agent->name); - g_free(agent->path); - g_free(agent); -} - static void change_state(struct audio_device *dev, gateway_state_t new_state) { struct gateway *gw = dev->gateway; @@ -146,8 +133,19 @@ static void change_state(struct audio_device *dev, gateway_state_t new_state) void gateway_set_state(struct audio_device *dev, gateway_state_t new_state) { + struct gateway *gw = dev->gateway; + switch (new_state) { case GATEWAY_STATE_DISCONNECTED: + if (gw->msg) { + DBusMessage *reply; + + reply = btd_error_failed(gw->msg, "Connect failed"); + g_dbus_send_message(dev->conn, reply); + dbus_message_unref(gw->msg); + gw->msg = NULL; + } + gateway_close(dev); break; case GATEWAY_STATE_CONNECTING: @@ -157,41 +155,6 @@ void gateway_set_state(struct audio_device *dev, gateway_state_t new_state) } } -static void agent_disconnect(struct audio_device *dev, struct hf_agent *agent) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(agent->name, agent->path, - "org.bluez.HandsfreeAgent", "Release"); - - g_dbus_send_message(dev->conn, msg); -} - -static gboolean agent_sendfd(struct hf_agent *agent, int fd, - DBusPendingCallNotifyFunction notify, void *data) -{ - struct audio_device *dev = data; - struct gateway *gw = dev->gateway; - DBusMessage *msg; - DBusPendingCall *call; - - msg = dbus_message_new_method_call(agent->name, agent->path, - "org.bluez.HandsfreeAgent", "NewConnection"); - - dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd, - DBUS_TYPE_UINT16, &gw->version, - DBUS_TYPE_INVALID); - - if (dbus_connection_send_with_reply(dev->conn, msg, &call, -1) == FALSE) - return FALSE; - - dbus_pending_call_set_notify(call, notify, dev, NULL); - dbus_pending_call_unref(call); - dbus_message_unref(msg); - - return TRUE; -} - static unsigned int connect_cb_new(struct gateway *gw, gateway_stream_cb_t func, void *user_data) @@ -266,175 +229,54 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) run_connect_cb(dev, NULL); } -static gboolean rfcomm_disconnect_cb(GIOChannel *chan, GIOCondition cond, - struct audio_device *dev) -{ - if (cond & G_IO_NVAL) - return FALSE; - - gateway_close(dev); - - return FALSE; -} - -static void newconnection_reply(DBusPendingCall *call, void *data) +void gateway_slc_complete(struct audio_device *dev) { - struct audio_device *dev = data; - struct gateway *gw = dev->gateway; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError derr; - - if (!dev->gateway->rfcomm) { - DBG("RFCOMM disconnected from server before agent reply"); - goto done; - } - - dbus_error_init(&derr); - if (!dbus_set_error_from_message(&derr, reply)) { - DBG("Agent reply: file descriptor passed successfully"); - g_io_add_watch(gw->rfcomm, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) rfcomm_disconnect_cb, dev); - change_state(dev, GATEWAY_STATE_CONNECTED); - goto done; - } - - DBG("Agent reply: %s", derr.message); - - dbus_error_free(&derr); - gateway_close(dev); - -done: - dbus_message_unref(reply); -} - -static void rfcomm_connect_cb(GIOChannel *chan, GError *err, - gpointer user_data) -{ - struct audio_device *dev = user_data; struct gateway *gw = dev->gateway; DBusMessage *reply; - int sk, ret; - - if (err) { - error("connect(): %s", err->message); - goto fail; - } - - if (!gw->agent) { - error("Handsfree Agent not registered"); - goto fail; - } - - sk = g_io_channel_unix_get_fd(chan); - if (gw->rfcomm == NULL) - gw->rfcomm = g_io_channel_ref(chan); + DBG("Service Level Connection established"); - ret = agent_sendfd(gw->agent, sk, newconnection_reply, dev); + change_state(dev, GATEWAY_STATE_CONNECTED); if (!gw->msg) return; - if (ret) - reply = dbus_message_new_method_return(gw->msg); - else - reply = btd_error_failed(gw->msg, "Can't pass file descriptor"); - + reply = dbus_message_new_method_return(gw->msg); g_dbus_send_message(dev->conn, reply); - - return; - -fail: - if (gw->msg) { - DBusMessage *reply; - reply = btd_error_failed(gw->msg, "Connect failed"); - g_dbus_send_message(dev->conn, reply); - } - - gateway_close(dev); -} - -static int get_remote_profile_version(sdp_record_t *rec) -{ - uuid_t uuid; - sdp_list_t *profiles; - sdp_profile_desc_t *desc; - int ver = 0; - - sdp_uuid16_create(&uuid, HANDSFREE_PROFILE_ID); - - sdp_get_profile_descs(rec, &profiles); - if (profiles == NULL) - goto done; - - desc = profiles->data; - - if (sdp_uuid16_cmp(&desc->uuid, &uuid) == 0) - ver = desc->version; - - sdp_list_free(profiles, free); - -done: - return ver; + dbus_message_unref(gw->msg); + gw->msg = NULL; } -static void get_incoming_record_cb(sdp_list_t *recs, int err, - gpointer user_data) +void gateway_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct audio_device *dev = user_data; struct gateway *gw = dev->gateway; - GError *gerr = NULL; + char hs_address[18]; + void *agent; - if (err < 0) { - error("Unable to get service record: %s (%d)", strerror(-err), - -err); - goto fail; + if (err) { + error("%s", err->message); + goto failed; } - if (!recs || !recs->data) { - error("No records found"); - goto fail; - } + ba2str(&dev->dst, hs_address); - gw->version = get_remote_profile_version(recs->data); - if (gw->version == 0) - goto fail; - - rfcomm_connect_cb(gw->incoming, gerr, dev); - return; - -fail: - gateway_close(dev); -} - -static void unregister_incoming(gpointer user_data) -{ - struct audio_device *dev = user_data; - struct gateway *gw = dev->gateway; - - if (gw->incoming) { - g_io_channel_unref(gw->incoming); - gw->incoming = NULL; - } -} + if (gw->rfcomm == NULL) + gw->rfcomm = g_io_channel_ref(chan); -static void rfcomm_incoming_cb(GIOChannel *chan, GError *err, - gpointer user_data) -{ - struct audio_device *dev = user_data; - struct gateway *gw = dev->gateway; - uuid_t uuid; + agent = telephony_agent_by_uuid(device_get_adapter(dev->btd_dev), + gw->connecting_uuid); + gw->slc = telephony_device_connecting(chan, dev, agent); + gw->connecting_uuid = NULL; + telephony_set_media_transport_path(gw->slc, gw->connecting_path); + gw->connecting_path = NULL; - gw->incoming = g_io_channel_ref(chan); + DBG("%s: Connected to %s", dev->path, hs_address); - sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID); - if (bt_search_service(&dev->src, &dev->dst, &uuid, - get_incoming_record_cb, dev, - unregister_incoming) == 0) - return; + return; - unregister_incoming(dev); - gateway_close(dev); +failed: + gateway_set_state(dev, GATEWAY_STATE_DISCONNECTED); } static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) @@ -471,13 +313,6 @@ static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) goto fail; } - gw->version = get_remote_profile_version(recs->data); - if (gw->version == 0) { - error("Unable to get profile version from record"); - err = -EINVAL; - goto fail; - } - memcpy(&uuid, classes->data, sizeof(uuid)); sdp_list_free(classes, free); @@ -498,7 +333,7 @@ static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) goto fail; } - io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &gerr, + io = bt_io_connect(BT_IO_RFCOMM, gateway_connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &dev->src, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_CHANNEL, ch, @@ -516,6 +351,8 @@ fail: DBusMessage *reply = btd_error_failed(gw->msg, gerr ? gerr->message : strerror(-err)); g_dbus_send_message(dev->conn, reply); + dbus_message_unref(gw->msg); + gw->msg = NULL; } gateway_close(dev); @@ -541,9 +378,17 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg, struct gateway *gw = au_dev->gateway; int err; - if (!gw->agent) + if (gw->state == GATEWAY_STATE_CONNECTING) + return btd_error_in_progress(msg); + else if (gw->state > GATEWAY_STATE_CONNECTING) + return btd_error_already_connected(msg); + + if (telephony_agent_by_uuid(device_get_adapter(au_dev->btd_dev), + HFP_HS_UUID) == NULL) return btd_error_agent_not_available(msg); + gw->connecting_uuid = HFP_HS_UUID; + err = get_records(au_dev); if (err < 0) return btd_error_failed(msg, strerror(-err)); @@ -608,17 +453,6 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg, return reply; } -static void agent_exited(DBusConnection *conn, void *data) -{ - struct gateway *gateway = data; - struct hf_agent *agent = gateway->agent; - - DBG("Agent %s exited", agent->name); - - agent_free(agent); - gateway->agent = NULL; -} - static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -650,71 +484,10 @@ static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg, return reply; } -static DBusMessage *register_agent(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - struct gateway *gw = device->gateway; - struct hf_agent *agent; - const char *path, *name; - - if (gw->agent) - return btd_error_already_exists(msg); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - name = dbus_message_get_sender(msg); - agent = g_new0(struct hf_agent, 1); - - agent->name = g_strdup(name); - agent->path = g_strdup(path); - - agent->watch = g_dbus_add_disconnect_watch(conn, name, - agent_exited, gw, NULL); - - gw->agent = agent; - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *unregister_agent(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - struct gateway *gw = device->gateway; - const char *path; - - if (!gw->agent) - goto done; - - if (strcmp(gw->agent->name, dbus_message_get_sender(msg)) != 0) - return btd_error_not_authorized(msg); - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - if (strcmp(gw->agent->path, path) != 0) - return btd_error_does_not_exist(msg); - - g_dbus_remove_watch(device->conn, gw->agent->watch); - - agent_free(gw->agent); - gw->agent = NULL; - -done: - return dbus_message_new_method_return(msg); -} - static GDBusMethodTable gateway_methods[] = { { "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC }, { "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC }, { "GetProperties", "", "a{sv}", ag_get_properties }, - { "RegisterAgent", "o", "", register_agent }, - { "UnregisterAgent", "o", "", unregister_agent }, { NULL, NULL, NULL, NULL } }; @@ -738,9 +511,6 @@ static void path_unregister(void *data) void gateway_unregister(struct audio_device *dev) { - if (dev->gateway->agent) - agent_disconnect(dev, dev->gateway->agent); - g_dbus_unregister_interface(dev->conn, dev->path, AUDIO_GATEWAY_INTERFACE); } @@ -791,6 +561,45 @@ int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io) return 0; } +GIOChannel *gateway_get_rfcomm(struct audio_device *dev) +{ + struct gateway *gw = dev->gateway; + + return gw->rfcomm; +} + +void gateway_set_connecting_uuid(struct audio_device *dev, const char *uuid) +{ + struct gateway *gw = dev->gateway; + + gw->connecting_uuid = uuid; +} + +void gateway_set_media_transport_path(struct audio_device *dev, + const char *path) +{ + struct gateway *gw = dev->gateway; + + DBG("MediaTransport path: %s", path); + + if (gw->slc == NULL) { + gw->connecting_path = path; + return; + } + + telephony_set_media_transport_path(gw->slc, path); +} + +const char *gateway_get_telephony_agent_name(struct audio_device *dev) +{ + struct gateway *gw = dev->gateway; + + if (gw == NULL || gw->slc == NULL) + return NULL; + + return telephony_get_agent_name(gw->slc); +} + int gateway_connect_sco(struct audio_device *dev, GIOChannel *io) { struct gateway *gw = dev->gateway; @@ -808,21 +617,6 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io) return 0; } -void gateway_start_service(struct audio_device *dev) -{ - struct gateway *gw = dev->gateway; - GError *err = NULL; - - if (gw->rfcomm == NULL) - return; - - if (!bt_io_accept(gw->rfcomm, rfcomm_incoming_cb, dev, NULL, &err)) { - error("bt_io_accept: %s", err->message); - g_error_free(err); - gateway_close(dev); - } -} - static gboolean request_stream_cb(gpointer data) { run_connect_cb(data, NULL); diff --git a/audio/gateway.h b/audio/gateway.h index 77f5787..b42fdf1 100644 --- a/audio/gateway.h +++ b/audio/gateway.h @@ -74,3 +74,11 @@ gboolean gateway_remove_state_cb(unsigned int id); gateway_lock_t gateway_get_lock(struct audio_device *dev); gboolean gateway_lock(struct audio_device *dev, gateway_lock_t lock); gboolean gateway_unlock(struct audio_device *dev, gateway_lock_t lock); + +void gateway_connect_cb(GIOChannel *chan, GError *err, gpointer user_data); +void gateway_slc_complete(struct audio_device *dev); +void gateway_set_connecting_uuid(struct audio_device *dev, const char *uuid); +GIOChannel *gateway_get_rfcomm(struct audio_device *dev); +void gateway_set_media_transport_path(struct audio_device *dev, + const char *path); +const char *gateway_get_telephony_agent_name(struct audio_device *dev); diff --git a/audio/manager.c b/audio/manager.c index 0329f07..e11607c 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -94,8 +94,6 @@ typedef enum { struct audio_adapter { struct btd_adapter *btd_adapter; gboolean powered; - uint32_t hfp_hs_record_id; - GIOChannel *hfp_hs_server; gint ref; }; @@ -228,139 +226,10 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device) } } -static sdp_record_t *hfp_hs_record(uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_record_t *record; - sdp_list_t *aproto, *proto[2]; - sdp_data_t *channel; - - record = sdp_record_alloc(); - if (!record) - return NULL; - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(record, root); - - sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); - profile.version = 0x0105; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(record, aproto); - - sdp_set_info_attr(record, "Hands-Free", 0, 0); - - sdp_data_free(channel); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); - - return record; -} - -static void gateway_auth_cb(DBusError *derr, void *user_data) -{ - struct audio_device *device = user_data; - - if (derr && dbus_error_is_set(derr)) { - error("Access denied: %s", derr->message); - gateway_set_state(device, GATEWAY_STATE_DISCONNECTED); - } else { - char ag_address[18]; - - ba2str(&device->dst, ag_address); - DBG("Accepted AG connection from %s for %s", - ag_address, device->path); - - gateway_start_service(device); - } -} - -static void hf_io_cb(GIOChannel *chan, gpointer data) -{ - bdaddr_t src, dst; - GError *err = NULL; - uint8_t ch; - const char *server_uuid, *remote_uuid; - struct audio_device *device; - int perr; - - bt_io_get(chan, BT_IO_RFCOMM, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_DEST_BDADDR, &dst, - BT_IO_OPT_CHANNEL, &ch, - BT_IO_OPT_INVALID); - - if (err) { - error("%s", err->message); - g_error_free(err); - return; - } - - server_uuid = HFP_AG_UUID; - remote_uuid = HFP_HS_UUID; - - device = manager_get_device(&src, &dst, TRUE); - if (!device) - goto drop; - - if (!device->gateway) { - btd_device_add_uuid(device->btd_dev, remote_uuid); - if (!device->gateway) - goto drop; - } - - if (gateway_is_active(device)) { - DBG("Refusing new connection since one already exists"); - goto drop; - } - - if (gateway_connect_rfcomm(device, chan) < 0) { - error("Allocating new GIOChannel failed!"); - goto drop; - } - - perr = audio_device_request_authorization(device, server_uuid, - gateway_auth_cb, device); - if (perr < 0) { - DBG("Authorization denied!"); - gateway_set_state(device, GATEWAY_STATE_DISCONNECTED); - } - - return; - -drop: - g_io_channel_shutdown(chan, TRUE, NULL); -} static int gateway_server_init(struct audio_adapter *adapter) { +#if 0 uint8_t chan = DEFAULT_HFP_HS_CHANNEL; sdp_record_t *record; gboolean master = TRUE; @@ -410,6 +279,7 @@ static int gateway_server_init(struct audio_adapter *adapter) } adapter->hfp_hs_record_id = record->handle; +#endif return 0; } @@ -602,6 +472,7 @@ static void gateway_server_remove(struct btd_adapter *adapter) if (!adp) return; +#if 0 if (adp->hfp_hs_record_id) { remove_record_from_server(adp->hfp_hs_record_id); adp->hfp_hs_record_id = 0; @@ -611,6 +482,7 @@ static void gateway_server_remove(struct btd_adapter *adapter) g_io_channel_unref(adp->hfp_hs_server); adp->hfp_hs_server = NULL; } +#endif audio_adapter_unref(adp); } diff --git a/audio/media.c b/audio/media.c index 33eb14a..0f46837 100644 --- a/audio/media.c +++ b/audio/media.c @@ -606,6 +606,7 @@ static void gateway_state_changed(struct audio_device *dev, void *user_data) { struct media_endpoint *endpoint = user_data; + const char *path; DBG(""); @@ -621,6 +622,8 @@ static void gateway_state_changed(struct audio_device *dev, case GATEWAY_STATE_CONNECTING: set_configuration(endpoint, dev, NULL, 0, gateway_setconf_cb, dev, NULL); + path = media_transport_get_path(endpoint->transport); + gateway_set_media_transport_path(dev, path); break; case GATEWAY_STATE_CONNECTED: break; @@ -687,14 +690,18 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte } else if (strcasecmp(uuid, HFP_HS_UUID) == 0 || strcasecmp(uuid, HSP_HS_UUID) == 0) { struct audio_device *dev; + const char *t_path; endpoint->ag_watch = gateway_add_state_cb(gateway_state_changed, endpoint); dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY, AUDIO_GATEWAY_INTERFACE, TRUE); - if (dev) + if (dev) { set_configuration(endpoint, dev, NULL, 0, gateway_setconf_cb, dev, NULL); + t_path = media_transport_get_path(endpoint->transport); + gateway_set_media_transport_path(dev, t_path); + } } else { if (err) *err = -EINVAL; diff --git a/audio/telephony.c b/audio/telephony.c index a7b1478..8c7c406 100644 --- a/audio/telephony.c +++ b/audio/telephony.c @@ -44,6 +44,7 @@ #include "glib-helper.h" #include "sdp-client.h" #include "headset.h" +#include "gateway.h" #include "telephony.h" #include "dbus-common.h" #include "../src/adapter.h" @@ -295,6 +296,46 @@ done: dbus_message_unref(reply); } +static gboolean ag_dev_disconnect_cb(GIOChannel *chan, GIOCondition cond, + struct tel_device *dev) +{ + if (cond & G_IO_NVAL) + return FALSE; + + gateway_set_state(dev->au_dev, GATEWAY_STATE_DISCONNECTED); + + return FALSE; +} + +static void ag_newconnection_reply(DBusPendingCall *call, void *user_data) +{ + struct tel_device *dev = user_data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError derr; + + if (!dev->rfcomm) { + DBG("RFCOMM disconnected from server before agent reply"); + goto done; + } + + dbus_error_init(&derr); + if (!dbus_set_error_from_message(&derr, reply)) { + DBG("Agent reply: file descriptor passed successfully"); + g_io_add_watch(dev->rfcomm, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) ag_dev_disconnect_cb, dev); + gateway_slc_complete(dev->au_dev); + goto done; + } + + DBG("Agent reply: %s", derr.message); + + dbus_error_free(&derr); + gateway_set_state(dev->au_dev, GATEWAY_STATE_DISCONNECTED); + +done: + dbus_message_unref(reply); +} + static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) { struct tel_device *dev = user_data; @@ -412,6 +453,232 @@ const char *telephony_get_agent_name(void *slc) return dev->name; } +#if 0 +static sdp_record_t *hsp_hs_record(struct tel_agent * agent) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_record_t *record; + sdp_list_t *aproto, *proto[2]; + sdp_data_t *channel, *volume; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(0, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); + profile.version = agent->version; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &agent->properties->channel); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + volume = sdp_data_alloc(SDP_BOOL, &agent->features); + sdp_attr_add(record, SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL, volume); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(record, aproto); + + sdp_set_info_attr(record, "Headset", 0, 0); + + sdp_data_free(channel); + sdp_list_free(proto[0], 0); + sdp_list_free(proto[1], 0); + sdp_list_free(apseq, 0); + sdp_list_free(pfseq, 0); + sdp_list_free(aproto, 0); + sdp_list_free(root, 0); + sdp_list_free(svclass_id, 0); + + return record; +} +#endif + +static sdp_record_t *hfp_hs_record(struct tel_agent * agent) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t *record; + sdp_data_t *channel, *features; + uint16_t sdpfeat; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(0, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); + profile.version = agent->version; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &agent->properties->channel); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + sdpfeat = agent->features & 0x1F; + features = sdp_data_alloc(SDP_UINT16, &sdpfeat); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(record, aproto); + + sdp_set_info_attr(record, "Hands-Free", 0, 0); + + sdp_data_free(channel); + sdp_list_free(proto[0], 0); + sdp_list_free(proto[1], 0); + sdp_list_free(apseq, 0); + sdp_list_free(pfseq, 0); + sdp_list_free(aproto, 0); + sdp_list_free(root, 0); + sdp_list_free(svclass_id, 0); + + return record; +} + +static void gateway_auth_cb(DBusError *derr, void *user_data) +{ + struct audio_device *device = user_data; + GError *err = NULL; + GIOChannel *io; + + if (device->gw_preauth_id) { + g_source_remove(device->gw_preauth_id); + device->gw_preauth_id = 0; + } + + if (derr && dbus_error_is_set(derr)) { + error("Access denied: %s", derr->message); + gateway_set_state(device, GATEWAY_STATE_DISCONNECTED); + return; + } + + io = gateway_get_rfcomm(device); + + if (!bt_io_accept(io, gateway_connect_cb, device, NULL, &err)) { + error("bt_io_accept: %s", err->message); + g_error_free(err); + gateway_set_state(device, GATEWAY_STATE_DISCONNECTED); + return; + } +} + +static gboolean gateway_preauth_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + struct audio_device *device = user_data; + + DBG("Gateway disconnected during authorization"); + + audio_device_cancel_authorization(device, gateway_auth_cb, device); + + gateway_set_state(device, GATEWAY_STATE_DISCONNECTED); + + device->gw_preauth_id = 0; + + return FALSE; +} + +static void hf_confirm(GIOChannel *chan, gpointer data) +{ + struct tel_agent *agent = data; + struct audio_device *device; + bdaddr_t src, dst; + int perr; + GError *err = NULL; + uint8_t ch; + + bt_io_get(chan, BT_IO_RFCOMM, &err, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_CHANNEL, &ch, + BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + goto drop; + } + + device = manager_get_device(&src, &dst, TRUE); + if (!device) + goto drop; + + if (!device->gateway) { + btd_device_add_uuid(device->btd_dev, agent->properties->r_uuid); + if (!device->gateway) + goto drop; + } + + if (gateway_is_active(device)) { + DBG("Refusing new connection since one already exists"); + goto drop; + } + + gateway_set_connecting_uuid(device, agent->properties->uuid); + + if (gateway_connect_rfcomm(device, chan) < 0) { + error("Allocating new GIOChannel failed!"); + goto drop; + } + + perr = audio_device_request_authorization(device, + agent->properties->uuid, + gateway_auth_cb, device); + if (perr < 0) { + DBG("Authorization denied: %s", strerror(-perr)); + gateway_set_state(device, GATEWAY_STATE_DISCONNECTED); + return; + } + + device->gw_preauth_id = g_io_add_watch(chan, + G_IO_NVAL | G_IO_HUP | G_IO_ERR, + gateway_preauth_cb, device); + + return; + +drop: + g_io_channel_shutdown(chan, TRUE, NULL); +} + static sdp_record_t *hsp_ag_record(struct tel_agent * agent) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; @@ -673,6 +940,14 @@ static struct default_agent default_properties[] = { hsp_ag_record, ag_confirm, hs_newconnection_reply }, + { HFP_HS_UUID, + DEFAULT_HF_HS_CHANNEL, + HFP_AG_UUID, + HANDSFREE_AGW_SVCLASS_ID, + HANDSFREE_PROFILE_ID, + hfp_hs_record, + hf_confirm, + ag_newconnection_reply }, { HFP_AG_UUID, DEFAULT_HF_AG_CHANNEL, HFP_HS_UUID, diff --git a/audio/transport.c b/audio/transport.c index 33417b4..234617a 100644 --- a/audio/transport.c +++ b/audio/transport.c @@ -836,6 +836,10 @@ static gboolean check_telephony_agent_name(struct media_transport *transport, if (tel_agent != NULL && g_strcmp0(tel_agent, sender) == 0) return TRUE; + tel_agent = gateway_get_telephony_agent_name(transport->device); + if (tel_agent != NULL && g_strcmp0(tel_agent, sender) == 0) + return TRUE; + return FALSE; } diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt index cf2e730..afc1277 100644 --- a/doc/hfp-api.txt +++ b/doc/hfp-api.txt @@ -22,24 +22,6 @@ Methods void Connect() Returns all properties for the interface. See the properties section for available properties. - void RegisterAgent(object path) - - The object path defines the path the of the agent - that will be called when a new Handsfree connection - is established. - - If an application disconnects from the bus all of its - registered agents will be removed. - - void UnregisterAgent(object path) - - This unregisters the agent that has been previously - registered. The object path parameter must match the - same value that has been used on registration. - - Possible Errors: org.bluez.Error.Failed - org.bluez.Error.InvalidArguments - Signals PropertyChanged(string name, variant value) @@ -54,33 +36,3 @@ Properties string State [readonly] "connecting" "connected" "playing" - -HandsfreeAgent hierarchy -=============== - -Service unique name -Interface org.bluez.HandsfreeAgent -Object path freely definable - -Methods void NewConnection(filedescriptor fd, uint16 version) - - This method gets called whenever a new handsfree - connection has been established. The objectpath - contains the object path of the remote device. This - method assumes that DBus daemon with file descriptor - passing capability is being used. - - The agent should only return successfully once the - establishment of the service level connection (SLC) - has been completed. In the case of Handsfree this - means that BRSF exchange has been performed and - necessary initialization has been done. - - Possible Errors: org.bluez.Error.InvalidArguments - org.bluez.Error.Failed - - void Release() - - This method gets called whenever the service daemon - unregisters the agent or whenever the Adapter where - the HandsfreeAgent registers itself is removed. -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html