diff options
author | Johan Hedberg <johan.hedberg@nokia.com> | 2007-05-31 09:23:17 +0000 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@nokia.com> | 2007-05-31 09:23:17 +0000 |
commit | 04efa14f48878cd1c1a31afc106f782fc97277a0 (patch) | |
tree | 23ba3be8876475a0e853a87df45238c7001027a3 | |
parent | 11f21abbc245e090345f21734a7a00cdde087078 (diff) |
Bring the audio code up-to-date with the current API spec
-rw-r--r-- | audio/headset.c | 291 | ||||
-rw-r--r-- | audio/headset.h | 9 | ||||
-rw-r--r-- | audio/main.c | 2 | ||||
-rw-r--r-- | audio/manager.c | 291 | ||||
-rw-r--r-- | audio/manager.h | 18 |
5 files changed, 506 insertions, 105 deletions
diff --git a/audio/headset.c b/audio/headset.c index 4c2b8423..d9e26459 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -56,6 +56,7 @@ #include "headset.h" #define DEFAULT_HS_AG_CHANNEL 12 +#define DEFAULT_HF_AG_CHANNEL 13 #define RING_INTERVAL 3000 @@ -76,12 +77,20 @@ typedef enum { HEADSET_STATE_PLAYING, } headset_state_t; +typedef enum { + SVC_HEADSET, + SVC_HANDSFREE +} hs_type; + struct pending_connect { DBusMessage *msg; GIOChannel *io; }; struct headset { + uint32_t hsp_handle; + uint32_t hfp_handle; + int rfcomm_ch; GIOChannel *rfcomm; @@ -93,6 +102,8 @@ struct headset { int data_start; int data_length; + hs_type type; + headset_state_t state; struct pending_connect *pending_connect; }; @@ -100,9 +111,13 @@ struct headset { static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data); +static gboolean disable_hfp = FALSE; + static GIOChannel *hs_server = NULL; +static GIOChannel *hf_server = NULL; static uint32_t hs_record_id = 0; +static uint32_t hf_record_id = 0; static DBusConnection *connection = NULL; @@ -184,7 +199,7 @@ static void close_sco(audio_device_t *device) static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { - struct headset *hs = device->headset; + struct headset *hs; unsigned char buf[BUF_SIZE]; char *cr, rsp[BUF_SIZE]; gsize bytes_read = 0; @@ -195,6 +210,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, if (cond & G_IO_NVAL) return FALSE; + hs = device->headset; + if (cond & (G_IO_ERR | G_IO_HUP)) goto failed; @@ -343,11 +360,13 @@ static void auth_callback(DBusPendingCall *call, void *data) static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { - struct headset *hs = device->headset; + struct headset *hs; if (cond & G_IO_NVAL) return FALSE; + hs = device->headset; + error("Audio connection got disconnected"); if (hs->sco) @@ -501,6 +520,8 @@ static int rfcomm_connect(audio_device_t *device, int *err) assert(hs != NULL && hs->pending_connect != NULL && hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); + hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; + ba2str(&device->bda, address); debug("Connecting to %s channel %d", address, hs->rfcomm_ch); @@ -567,7 +588,7 @@ failed: return -1; } -static int create_ag_record(sdp_buf_t *buf, uint8_t ch) +static int create_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; @@ -607,7 +628,76 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch) aproto = sdp_list_append(0, apseq); sdp_set_access_protos(&record, aproto); - sdp_set_info_attr(&record, "Headset", 0, 0); + sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 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); + sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free); + sdp_list_free(record.pattern, free); + + return ret; +} + +static int create_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + uint16_t u16 = 0x0009; + sdp_data_t *channel, *features; + uint8_t netid = 0x01; + sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); + int ret; + + memset(&record, 0, sizeof(sdp_record_t)); + + 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_AGW_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]); + + features = sdp_data_alloc(SDP_UINT16, &u16); + 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 Audio Gateway", 0, 0); + + sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); if (sdp_gen_record_pdu(&record, buf) < 0) ret = -1; @@ -628,12 +718,11 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch) return ret; } -static uint32_t headset_add_ag_record(uint8_t channel) +static uint32_t headset_add_ag_record(uint8_t channel, sdp_buf_t *buf) { DBusMessage *msg, *reply; DBusError derr; dbus_uint32_t rec_id; - sdp_buf_t buf; msg = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", "AddServiceRecord"); @@ -642,20 +731,13 @@ static uint32_t headset_add_ag_record(uint8_t channel) return 0; } - if (create_ag_record(&buf, channel) < 0) { - error("Unable to allocate new service record"); - dbus_message_unref(msg); - return 0; - } - dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &buf.data, buf.data_size, DBUS_TYPE_INVALID); + &buf->data, buf->data_size, DBUS_TYPE_INVALID); dbus_error_init(&derr); reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); - free(buf.data); dbus_message_unref(msg); if (dbus_error_is_set(&derr) || dbus_set_error_from_message(&derr, reply)) { @@ -901,7 +983,7 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, } if (hs->state > HEADSET_STATE_CONNECTED) - hs_stop(NULL, NULL, hs); + hs_stop(NULL, NULL, device); if (hs->rfcomm) { g_io_channel_close(hs->rfcomm); @@ -1344,28 +1426,14 @@ static DBusSignalVTable headset_signals[] = { { NULL, NULL } }; -headset_t *headset_init(const char *object_path, sdp_record_t *record) +static void headset_set_channel(headset_t *headset, sdp_record_t *record) { int ch; sdp_list_t *protos; - headset_t *headset; - - if (!dbus_connection_register_interface(connection, object_path, - AUDIO_HEADSET_INTERFACE, - headset_methods, - headset_signals, NULL)) - return NULL; - - headset = g_new0(headset_t, 1); - - headset->rfcomm_ch = -1; - - if (!record) - return headset; if (sdp_get_access_protos(record, &protos) < 0) { error("Unable to get access protos from headset record"); - return headset; + return; } ch = sdp_get_proto_port(protos, RFCOMM_UUID); @@ -1378,10 +1446,113 @@ headset_t *headset_init(const char *object_path, sdp_record_t *record) debug("Discovered Headset service on RFCOMM channel %d", ch); } else error("Unable to get RFCOMM channel from Headset record"); +} + +void headset_update(headset_t *headset, sdp_record_t *record, uint16_t svc) +{ + switch (svc) { + case HANDSFREE_SVCLASS_ID: + if (disable_hfp) { + debug("Ignoring Handsfree record since HFP support" + " has been disabled"); + return; + } + + if (headset->hfp_handle && + (headset->hfp_handle != record->handle)) { + error("More than one HFP record found on device"); + return; + } + + headset->hfp_handle = record->handle; + break; + + case HEADSET_SVCLASS_ID: + if (headset->hsp_handle && + (headset->hsp_handle != record->handle)) { + error("More than one HSP record found on device"); + return; + } + + headset->hsp_handle = record->handle; + + /* Ignore this record if we already have access to HFP */ + if (headset->hfp_handle) + return; + + break; + + default: + debug("Invalid record passed to headset_update"); + return; + } + + headset_set_channel(headset, record); +} + +headset_t *headset_init(const char *object_path, sdp_record_t *record, + uint16_t svc) +{ + headset_t *headset; + + headset = g_new0(headset_t, 1); + headset->rfcomm_ch = -1; + + if (!record) + goto register_iface; + + switch (svc) { + case HANDSFREE_SVCLASS_ID: + if (disable_hfp) { + debug("Ignoring Handsfree record since HFP support" + " has been disabled"); + g_free(headset); + return NULL; + } + + headset->hfp_handle = record->handle; + break; + + case HEADSET_SVCLASS_ID: + headset->hsp_handle = record->handle; + break; + + default: + debug("Invalid record passed to headset_init"); + g_free(headset); + return NULL; + } + +register_iface: + if (!dbus_connection_register_interface(connection, object_path, + AUDIO_HEADSET_INTERFACE, + headset_methods, + headset_signals, NULL)) { + g_free(headset); + return NULL; + } + + if (record) + headset_set_channel(headset, record); return headset; } +void headset_free(const char *object_path) +{ + audio_device_t *device; + + if (!dbus_connection_get_object_user_data(connection, object_path, + (void *) &device)) + return; + + if (device->headset->state != HEADSET_STATE_DISCONNECTED) + hs_disconnect(NULL, NULL, device); + + g_free(device->headset); + device->headset = NULL; +} + static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) { int srv_sk, cli_sk; @@ -1434,6 +1605,11 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * return TRUE; } + if (chan == hs_server) + hs->type = SVC_HEADSET; + else + hs->type = SVC_HANDSFREE; + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", "RequestAuthorization"); if (!auth) { @@ -1490,7 +1666,7 @@ static GIOChannel *server_socket(uint8_t *channel) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = 0; + addr.rc_channel = channel ? *channel : 0; if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { error("server bind: %s", strerror(errno), errno); @@ -1526,9 +1702,10 @@ gboolean headset_is_connected(headset_t *headset) return FALSE; } -int headset_server_init(DBusConnection *conn) +int headset_server_init(DBusConnection *conn, gboolean no_hfp) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; + sdp_buf_t buf; connection = dbus_connection_ref(conn); @@ -1536,11 +1713,15 @@ int headset_server_init(DBusConnection *conn) if (!hs_server) return -1; - if (!hs_record_id) - hs_record_id = headset_add_ag_record(chan); + if (create_hsp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + hs_record_id = headset_add_ag_record(chan, &buf); + free(buf.data); if (!hs_record_id) { - error("Unable to register service record"); + error("Unable to register HS AG service record"); g_io_channel_unref(hs_server); hs_server = NULL; return -1; @@ -1549,6 +1730,34 @@ int headset_server_init(DBusConnection *conn) g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) headset_server_io_cb, NULL); + disable_hfp = no_hfp; + + if (disable_hfp) + return 0; + + chan = DEFAULT_HF_AG_CHANNEL; + + hf_server = server_socket(&chan); + if (!hf_server) + return -1; + + if (create_hfp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + hf_record_id = headset_add_ag_record(chan, &buf); + free(buf.data); + if (!hf_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hf_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) headset_server_io_cb, NULL); + return 0; } @@ -1564,6 +1773,16 @@ void headset_exit(void) hs_server = NULL; } + if (hf_record_id) { + headset_remove_ag_record(hf_record_id); + hf_record_id = 0; + } + + if (hf_server) { + g_io_channel_unref(hf_server); + hf_server = NULL; + } + dbus_connection_unref(connection); connection = NULL; } diff --git a/audio/headset.h b/audio/headset.h index 3ccbf8b2..c02f36a3 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -33,11 +33,16 @@ typedef struct headset headset_t; -headset_t *headset_init(const char *object_path, sdp_record_t *record); +headset_t *headset_init(const char *object_path, sdp_record_t *record, + uint16_t svc); + +void headset_free(const char *object_path); + +void headset_update(headset_t *headset, sdp_record_t *record, uint16_t svc); gboolean headset_is_connected(headset_t *headset); -int headset_server_init(DBusConnection *conn); +int headset_server_init(DBusConnection *conn, gboolean disable_hfp); void headset_exit(void); diff --git a/audio/main.c b/audio/main.c index 98dc1d4f..80e3fabf 100644 --- a/audio/main.c +++ b/audio/main.c @@ -84,7 +84,7 @@ int main(int argc, char *argv[]) exit(1); } - if (headset_server_init(conn) < 0) { + if (headset_server_init(conn, FALSE) < 0) { error("Headset initialization failed!"); exit(1); } diff --git a/audio/manager.c b/audio/manager.c index bafc7d9d..85d52e86 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -27,6 +27,7 @@ #include <stdio.h> #include <stdlib.h> +#include <stdarg.h> #include <errno.h> #include <unistd.h> #include <stdint.h> @@ -49,6 +50,16 @@ #include "manager.h" typedef enum { + HEADSET = 1 << 0, + GATEWAY = 1 << 1, + SINK = 1 << 2, + SOURCE = 1 << 3, + CONTROL = 1 << 4, + TARGET = 1 << 5, + INVALID = 1 << 6 +} audio_service_type; + +typedef enum { GENERIC_AUDIO = 0, ADVANCED_AUDIO, AV_REMOTE, @@ -58,10 +69,10 @@ typedef enum { struct audio_sdp_data { audio_device_t *device; - DBusMessage *msg; + DBusMessage *msg; /* Method call or NULL */ - GSList *handles; - GSList *records; + GSList *handles; /* uint32_t * */ + GSList *records; /* sdp_record_t * */ audio_sdp_state_t state; }; @@ -224,8 +235,8 @@ static audio_device_t *create_device(bdaddr_t *bda) static void remove_device(audio_device_t *device) { devices = g_slist_remove(devices, device); + headset_free(device->object_path); dbus_connection_destroy_object_path(connection, device->object_path); - g_free(device->headset); g_free(device); } @@ -253,6 +264,40 @@ static gboolean add_device(audio_device_t *device) return TRUE; } +static uint16_t get_service_uuid(const sdp_record_t *record) +{ + sdp_list_t *classes; + uuid_t uuid; + uint16_t uuid16 = 0; + + if (sdp_get_service_classes(record, &classes) < 0) { + error("Unable to get service classes from record"); + return 0; + } + + memcpy(&uuid, classes->data, sizeof(uuid)); + + if (!sdp_uuid128_to_uuid(&uuid)) { + error("Not a 16 bit UUID"); + sdp_list_free(classes, free); + return 0; + } + + if (uuid.type == SDP_UUID32) { + if (uuid.value.uuid32 > 0xFFFF) { + error("Not a 16 bit UUID"); + goto done; + } + uuid16 = (uint16_t) uuid.value.uuid32; + } else + uuid16 = uuid.value.uuid16; + +done: + sdp_list_free(classes, free); + + return uuid16; +} + void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba) { char address[18], *addr_ptr = address; @@ -290,39 +335,18 @@ void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba) static void handle_record(sdp_record_t *record, audio_device_t *device) { - sdp_list_t *classes; - uuid_t uuid; uint16_t uuid16; - if (sdp_get_service_classes(record, &classes) < 0) { - error("Unable to get service classes from record"); - return; - } - - memcpy(&uuid, classes->data, sizeof(uuid)); - - if (!sdp_uuid128_to_uuid(&uuid)) { - error("Not a 16 bit UUID"); - goto done; - } - - if (uuid.type == SDP_UUID32) { - if (uuid.value.uuid32 > 0xFFFF) { - error("Not a 16 bit UUID"); - goto done; - } - uuid16 = (uint16_t) uuid.value.uuid32; - } else - uuid16 = uuid.value.uuid16; + uuid16 = get_service_uuid(record); switch (uuid16) { case HEADSET_SVCLASS_ID: debug("Found Headset record"); if (device->headset) - debug("Multiple Headset records found"); + headset_update(device->headset, record, uuid16); else device->headset = headset_init(device->object_path, - record); + record, uuid16); break; case HEADSET_AGW_SVCLASS_ID: debug("Found Headset AG record"); @@ -349,15 +373,45 @@ static void handle_record(sdp_record_t *record, audio_device_t *device) debug("Unrecognized UUID: 0x%04X", uuid16); break; } +} -done: - sdp_list_free(classes, free); +static gint record_iface_cmp(gconstpointer a, gconstpointer b) +{ + const sdp_record_t *record = a; + const char *interface = b; + + switch (get_service_uuid(record)) { + case HEADSET_SVCLASS_ID: + case HANDSFREE_SVCLASS_ID: + return strcmp(interface, AUDIO_HEADSET_INTERFACE); + + case HEADSET_AGW_SVCLASS_ID: + case HANDSFREE_AGW_SVCLASS_ID: + return strcmp(interface, AUDIO_GATEWAY_INTERFACE); + + case AUDIO_SINK_SVCLASS_ID: + return strcmp(interface, AUDIO_SINK_INTERFACE); + + case AUDIO_SOURCE_SVCLASS_ID: + return strcmp(interface, AUDIO_SOURCE_INTERFACE); + + case AV_REMOTE_SVCLASS_ID: + return strcmp(interface, AUDIO_CONTROL_INTERFACE); + + case AV_REMOTE_TARGET_SVCLASS_ID: + return strcmp(interface, AUDIO_TARGET_INTERFACE); + + default: + return -1; + } } static void finish_sdp(struct audio_sdp_data *data, gboolean success) { - const char *path; - DBusMessage *reply; + const char *path, *addr; + char **required; + int required_len, i; + DBusMessage *reply = NULL; debug("Audio service discovery completed with %s", success ? "success" : "failure"); @@ -367,6 +421,36 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) if (!success) goto done; + if (!data->msg) + goto update; + + if (!dbus_message_get_args(data->msg, NULL, + DBUS_TYPE_STRING, &addr, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &required, &required_len, + DBUS_TYPE_INVALID)) { + error("Unable to get message args"); + success = FALSE; + goto done; + } + + for (i = 0; i < required_len; i++) { + const char *iface = required[i]; + + if (g_slist_find_custom(data->records, iface, record_iface_cmp)) + continue; + + debug("Required interface %s not supported", iface); + success = FALSE; + err_not_supported(connection, data->msg); + dbus_free_string_array(required); + goto done; + } + + dbus_free_string_array(required); + + path = data->device->object_path; + reply = dbus_message_new_method_return(data->msg); if (!reply) { success = FALSE; @@ -374,25 +458,28 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) goto done; } - path = data->device->object_path; - add_device(data->device); + +update: g_slist_foreach(data->records, (GFunc) handle_record, data->device); - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "DeviceCreated", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + if (reply) { + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DeviceCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - send_message_and_unref(connection, reply); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + send_message_and_unref(connection, reply); + } done: if (!success) free_device(data->device); - dbus_message_unref(data->msg); + if (data->msg) + dbus_message_unref(data->msg); g_slist_foreach(data->handles, (GFunc) g_free, NULL); g_slist_free(data->handles); g_slist_foreach(data->records, (GFunc) sdp_record_free, NULL); @@ -654,19 +741,21 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) } if (!device->headset) - device->headset = headset_init(device->object_path, NULL); + device->headset = headset_init(device->object_path, NULL, 0); if (!device->headset) return NULL; path = device->object_path; - if (created) + if (created) { dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DeviceCreated", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); + resolve_services(NULL, device); + } dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, @@ -686,17 +775,62 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) return device; } +static gboolean device_supports_interface(audio_device_t *device, + const char *iface) +{ + if (strcmp(iface, AUDIO_HEADSET_INTERFACE) == 0) + return device->headset ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_GATEWAY_INTERFACE) == 0) + return device->gateway ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_SOURCE_INTERFACE) == 0) + return device->gateway ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_SINK_INTERFACE) == 0) + return device->sink ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_CONTROL_INTERFACE) == 0) + return device->control ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_TARGET_INTERFACE) == 0) + return device->target ? TRUE : FALSE; + + debug("Unknown interface %s", iface); + + return FALSE; +} + +static gboolean device_matches(audio_device_t *device, char **interfaces) +{ + int i; + + for (i = 0; interfaces[i]; i++) { + if (device_supports_interface(device, interfaces[i])) + continue; + debug("Device does not support interface %s", interfaces[i]); + return FALSE; + } + + return TRUE; +} + static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg, void *data) { - const char *address; + const char *address, *path; + char **required; + int required_len; bdaddr_t bda; audio_device_t *device; + DBusMessage *reply; DBusError derr; dbus_error_init(&derr); dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &address, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &required, &required_len, DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); @@ -707,19 +841,29 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg str2ba(address, &bda); device = find_device(&bda); - if (device) { - const char *path = device->object_path; - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - return send_message_and_unref(conn, reply); + if (!device) { + device = create_device(&bda); + dbus_free_string_array(required); + return resolve_services(msg, device); } - device = create_device(&bda); + if (!device_matches(device, required)) { + dbus_free_string_array(required); + return err_not_supported(conn, msg); + } + + dbus_free_string_array(required); + + path = device->object_path; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - return resolve_services(msg, device); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + + return send_message_and_unref(conn, reply); } static DBusHandlerResult am_list_devices(DBusConnection *conn, @@ -728,7 +872,21 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, { DBusMessageIter iter, array_iter; DBusMessage *reply; + DBusError derr; GSList *l; + char **required; + int required_len; + + dbus_error_init(&derr); + dbus_message_get_args(msg, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &required, &required_len, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&derr)) { + err_invalid_args(connection, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } reply = dbus_message_new_method_return(msg); if (!reply) @@ -743,6 +901,9 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, audio_device_t *device = l->data; const char *path; + if (!device_matches(device, required)) + continue; + path = device->object_path; dbus_message_iter_append_basic(&array_iter, @@ -751,6 +912,8 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, dbus_message_iter_close_container(&iter, &array_iter); + dbus_free_string_array(required); + return send_message_and_unref(connection, reply); } @@ -786,12 +949,14 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms } } - device->headset = headset_init(device->object_path, NULL); if (!device->headset) { - remove_device(device); - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to init Headset interface"); + device->headset = headset_init(device->object_path, NULL, 0); + if (!device->headset) { + remove_device(device); + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to init Headset interface"); + } } path = device->object_path; @@ -1005,11 +1170,11 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes static DBusMethodVTable manager_methods[] = { { "CreateDevice", am_create_device, - "s", "s" }, + "sas", "s" }, { "RemoveDevice", am_remove_device, "s", "" }, { "ListDevices", am_list_devices, - "", "as" }, + "as", "as" }, { "CreateHeadset", am_create_headset, "s", "s" }, { "RemoveHeadset", am_remove_headset, diff --git a/audio/manager.h b/audio/manager.h index 45c5fa5f..78ec63ab 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -48,18 +48,30 @@ #define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" #define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" +/* Move these to respective .h files once they exist */ +#define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" +#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" +#define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" +#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" +#define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" +typedef struct gateway gateway_t; +typedef struct sink sink_t; +typedef struct source source_t; +typedef struct control control_t; +typedef struct target target_t; + typedef struct audio_device { char object_path[128]; bdaddr_t bda; headset_t *headset; -/* - audio_gw_t *audio_gw; + + gateway_t *gateway; sink_t *sink; source_t *source; control_t *control; target_t *target; -*/ + } audio_device_t; audio_device_t *manager_headset_connected(bdaddr_t *bda); |