summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2007-05-31 09:23:17 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2007-05-31 09:23:17 +0000
commit04efa14f48878cd1c1a31afc106f782fc97277a0 (patch)
tree23ba3be8876475a0e853a87df45238c7001027a3
parent11f21abbc245e090345f21734a7a00cdde087078 (diff)
Bring the audio code up-to-date with the current API spec
-rw-r--r--audio/headset.c291
-rw-r--r--audio/headset.h9
-rw-r--r--audio/main.c2
-rw-r--r--audio/manager.c291
-rw-r--r--audio/manager.h18
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);