mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	add missing files
This commit is contained in:
		
							parent
							
								
									a71fa021a3
								
							
						
					
					
						commit
						4a75002ebe
					
				
					 2 changed files with 882 additions and 0 deletions
				
			
		
							
								
								
									
										805
									
								
								src/modules/bluetooth/bluetooth-util.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										805
									
								
								src/modules/bluetooth/bluetooth-util.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,805 @@
 | 
			
		|||
/***
 | 
			
		||||
  This file is part of PulseAudio.
 | 
			
		||||
 | 
			
		||||
  Copyright 2008 Joao Paulo Rechi Vita
 | 
			
		||||
 | 
			
		||||
  PulseAudio is free software; you can redistribute it and/or modify
 | 
			
		||||
  it under the terms of the GNU Lesser General Public License as
 | 
			
		||||
  published by the Free Software Foundation; either version 2 of the
 | 
			
		||||
  License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
  PulseAudio 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
 | 
			
		||||
  General Public License for more details.
 | 
			
		||||
 | 
			
		||||
  You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
  License along with PulseAudio; if not, write to the Free Software
 | 
			
		||||
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 | 
			
		||||
  USA.
 | 
			
		||||
***/
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <pulsecore/core-util.h>
 | 
			
		||||
#include <modules/dbus-util.h>
 | 
			
		||||
 | 
			
		||||
#include "bluetooth-util.h"
 | 
			
		||||
 | 
			
		||||
enum mode {
 | 
			
		||||
    MODE_FIND,
 | 
			
		||||
    MODE_GET,
 | 
			
		||||
    MODE_DISCOVER
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pa_bluetooth_discovery {
 | 
			
		||||
    DBusConnection *connection;
 | 
			
		||||
    PA_LLIST_HEAD(pa_dbus_pending, pending);
 | 
			
		||||
 | 
			
		||||
    enum mode mode;
 | 
			
		||||
 | 
			
		||||
    /* If mode == MODE_FIND look for a specific device by its address.
 | 
			
		||||
       If mode == MODE_GET look for a specific device by its path. */
 | 
			
		||||
    const char *looking_for;
 | 
			
		||||
    pa_bluetooth_device *found_device;
 | 
			
		||||
 | 
			
		||||
    /* If looking_for is NULL we do long-time discovery */
 | 
			
		||||
    pa_hashmap *devices;
 | 
			
		||||
    pa_bluetooth_device_callback_t callback;
 | 
			
		||||
    struct userdata *userdata;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static pa_bluetooth_uuid *uuid_new(const char *uuid) {
 | 
			
		||||
    pa_bluetooth_uuid *u;
 | 
			
		||||
 | 
			
		||||
    u = pa_xnew(pa_bluetooth_uuid, 1);
 | 
			
		||||
    u->uuid = pa_xstrdup(uuid);
 | 
			
		||||
    PA_LLIST_INIT(pa_bluetooth_uuid, u);
 | 
			
		||||
 | 
			
		||||
    return u;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void uuid_free(pa_bluetooth_uuid *u) {
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
 | 
			
		||||
    pa_xfree(u->uuid);
 | 
			
		||||
    pa_xfree(u);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_bluetooth_device* device_new(const char *path) {
 | 
			
		||||
    pa_bluetooth_device *d;
 | 
			
		||||
 | 
			
		||||
    d = pa_xnew(pa_bluetooth_device, 1);
 | 
			
		||||
 | 
			
		||||
    d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0;
 | 
			
		||||
 | 
			
		||||
    d->data = NULL;
 | 
			
		||||
 | 
			
		||||
    d->name = NULL;
 | 
			
		||||
    d->path = pa_xstrdup(path);
 | 
			
		||||
    d->paired = -1;
 | 
			
		||||
    d->alias = NULL;
 | 
			
		||||
    d->device_connected = -1;
 | 
			
		||||
    PA_LLIST_HEAD_INIT(pa_bluetooth_uuid, d->uuids);
 | 
			
		||||
    d->address = NULL;
 | 
			
		||||
    d->class = -1;
 | 
			
		||||
    d->trusted = -1;
 | 
			
		||||
 | 
			
		||||
    d->audio_sink_connected = -1;
 | 
			
		||||
 | 
			
		||||
    d->headset_connected = -1;
 | 
			
		||||
 | 
			
		||||
    return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_bluetooth_device_free(pa_bluetooth_device *d) {
 | 
			
		||||
    pa_bluetooth_uuid *u;
 | 
			
		||||
 | 
			
		||||
    pa_assert(d);
 | 
			
		||||
 | 
			
		||||
    while ((u = d->uuids)) {
 | 
			
		||||
        PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u);
 | 
			
		||||
        uuid_free(u);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_xfree(d->name);
 | 
			
		||||
    pa_xfree(d->path);
 | 
			
		||||
    pa_xfree(d->alias);
 | 
			
		||||
    pa_xfree(d->address);
 | 
			
		||||
    pa_xfree(d);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_bool_t device_is_loaded(pa_bluetooth_device *d) {
 | 
			
		||||
    pa_assert(d);
 | 
			
		||||
 | 
			
		||||
    return d->device_info_valid && d->audio_sink_info_valid && d->headset_info_valid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
 | 
			
		||||
    pa_assert(d);
 | 
			
		||||
 | 
			
		||||
    pa_assert(d->device_info_valid);
 | 
			
		||||
    pa_assert(d->audio_sink_info_valid);
 | 
			
		||||
    pa_assert(d->headset_info_valid);
 | 
			
		||||
 | 
			
		||||
    return d->device_info_valid > 0 &&
 | 
			
		||||
        (d->audio_sink_info_valid > 0 || d->headset_info_valid > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
 | 
			
		||||
    const char *key;
 | 
			
		||||
    DBusMessageIter variant_i;
 | 
			
		||||
 | 
			
		||||
    pa_assert(y);
 | 
			
		||||
    pa_assert(d);
 | 
			
		||||
    pa_assert(i);
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
 | 
			
		||||
        pa_log("Property name not a string.");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dbus_message_iter_get_basic(i, &key);
 | 
			
		||||
 | 
			
		||||
    if (!dbus_message_iter_next(i))  {
 | 
			
		||||
        pa_log("Property value missing");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
 | 
			
		||||
        pa_log("Property value not a variant.");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dbus_message_iter_recurse(i, &variant_i);
 | 
			
		||||
 | 
			
		||||
    pa_log_debug("Parsing property org.bluez.Device.%s", key);
 | 
			
		||||
 | 
			
		||||
    switch (dbus_message_iter_get_arg_type(&variant_i)) {
 | 
			
		||||
 | 
			
		||||
        case DBUS_TYPE_STRING: {
 | 
			
		||||
 | 
			
		||||
            const char *value;
 | 
			
		||||
            dbus_message_iter_get_basic(&variant_i, &value);
 | 
			
		||||
 | 
			
		||||
            if (pa_streq(key, "Name")) {
 | 
			
		||||
                pa_xfree(d->name);
 | 
			
		||||
                d->name = pa_xstrdup(value);
 | 
			
		||||
            } else if (pa_streq(key, "Alias")) {
 | 
			
		||||
                pa_xfree(d->alias);
 | 
			
		||||
                d->alias = pa_xstrdup(value);
 | 
			
		||||
            } else if (pa_streq(key, "Address")) {
 | 
			
		||||
                pa_xfree(d->address);
 | 
			
		||||
                d->address = pa_xstrdup(value);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pa_log_debug("Value %s", value);
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case DBUS_TYPE_BOOLEAN: {
 | 
			
		||||
 | 
			
		||||
            dbus_bool_t value;
 | 
			
		||||
            dbus_message_iter_get_basic(&variant_i, &value);
 | 
			
		||||
 | 
			
		||||
            if (pa_streq(key, "Paired"))
 | 
			
		||||
                d->paired = !!value;
 | 
			
		||||
            else if (pa_streq(key, "Connected"))
 | 
			
		||||
                d->device_connected = !!value;
 | 
			
		||||
            else if (pa_streq(key, "Trusted"))
 | 
			
		||||
                d->trusted = !!value;
 | 
			
		||||
 | 
			
		||||
            pa_log_debug("Value %s", pa_yes_no(value));
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case DBUS_TYPE_UINT32: {
 | 
			
		||||
 | 
			
		||||
            uint32_t value;
 | 
			
		||||
            dbus_message_iter_get_basic(&variant_i, &value);
 | 
			
		||||
 | 
			
		||||
            if (pa_streq(key, "Class"))
 | 
			
		||||
                d->class = (int) value;
 | 
			
		||||
 | 
			
		||||
            pa_log_debug("Value %u", (unsigned) value);
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case DBUS_TYPE_ARRAY: {
 | 
			
		||||
 | 
			
		||||
            DBusMessageIter ai;
 | 
			
		||||
            dbus_message_iter_recurse(&variant_i, &ai);
 | 
			
		||||
 | 
			
		||||
            if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
 | 
			
		||||
                pa_streq(key, "UUIDs")) {
 | 
			
		||||
 | 
			
		||||
                while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
 | 
			
		||||
                    pa_bluetooth_uuid *node;
 | 
			
		||||
                    const char *value;
 | 
			
		||||
 | 
			
		||||
                    dbus_message_iter_get_basic(&ai, &value);
 | 
			
		||||
                    node = uuid_new(value);
 | 
			
		||||
                    PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
 | 
			
		||||
 | 
			
		||||
                    if (!dbus_message_iter_next(&ai))
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusMessageIter *i) {
 | 
			
		||||
    const char *key;
 | 
			
		||||
    DBusMessageIter variant_i;
 | 
			
		||||
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
    pa_assert(connected);
 | 
			
		||||
    pa_assert(i);
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
 | 
			
		||||
        pa_log("Property name not a string.");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dbus_message_iter_get_basic(i, &key);
 | 
			
		||||
 | 
			
		||||
    if (!dbus_message_iter_next(i))  {
 | 
			
		||||
        pa_log("Property value missing");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
 | 
			
		||||
        pa_log("Property value not a variant.");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dbus_message_iter_recurse(i, &variant_i);
 | 
			
		||||
 | 
			
		||||
    pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key);
 | 
			
		||||
 | 
			
		||||
    switch (dbus_message_iter_get_arg_type(&variant_i)) {
 | 
			
		||||
 | 
			
		||||
        case DBUS_TYPE_BOOLEAN: {
 | 
			
		||||
 | 
			
		||||
            dbus_bool_t value;
 | 
			
		||||
            dbus_message_iter_get_basic(&variant_i, &value);
 | 
			
		||||
 | 
			
		||||
            if (pa_streq(key, "Connected"))
 | 
			
		||||
                *connected = !!value;
 | 
			
		||||
 | 
			
		||||
            pa_log_debug("Value %s", pa_yes_no(value));
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t good) {
 | 
			
		||||
    pa_assert(y);
 | 
			
		||||
    pa_assert(d);
 | 
			
		||||
 | 
			
		||||
    if (y->mode != MODE_DISCOVER)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (!device_is_loaded(d))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (!device_is_audio(d))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    y->callback(y->userdata, d, good);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
 | 
			
		||||
    DBusMessage *r;
 | 
			
		||||
    DBusMessageIter arg_i, element_i;
 | 
			
		||||
    pa_dbus_pending *p;
 | 
			
		||||
    pa_bluetooth_device *d;
 | 
			
		||||
    pa_bluetooth_discovery *y;
 | 
			
		||||
    int valid;
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(p = userdata);
 | 
			
		||||
    pa_assert_se(y = p->context_data);
 | 
			
		||||
    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
 | 
			
		||||
 | 
			
		||||
/*     pa_log_debug("Got %s.GetProperties response for %s", */
 | 
			
		||||
/*                  dbus_message_get_interface(p->message), */
 | 
			
		||||
/*                  dbus_message_get_path(p->message)); */
 | 
			
		||||
 | 
			
		||||
    d = p->call_data;
 | 
			
		||||
 | 
			
		||||
    valid = dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR ? -1 : 1;
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
 | 
			
		||||
        d->device_info_valid = valid;
 | 
			
		||||
    else if (dbus_message_is_method_call(p->message, "org.bluez.Headset", "GetProperties"))
 | 
			
		||||
        d->headset_info_valid = valid;
 | 
			
		||||
    else if (dbus_message_is_method_call(p->message, "org.bluez.AudioSink", "GetProperties"))
 | 
			
		||||
        d->audio_sink_info_valid = valid;
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
 | 
			
		||||
 | 
			
		||||
        if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD))
 | 
			
		||||
            pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
 | 
			
		||||
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!dbus_message_iter_init(r, &arg_i)) {
 | 
			
		||||
        pa_log("GetProperties reply has no arguments.");
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
 | 
			
		||||
        pa_log("GetProperties argument is not an array.");
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dbus_message_iter_recurse(&arg_i, &element_i);
 | 
			
		||||
    while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
 | 
			
		||||
 | 
			
		||||
        if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
 | 
			
		||||
            DBusMessageIter dict_i;
 | 
			
		||||
 | 
			
		||||
            dbus_message_iter_recurse(&element_i, &dict_i);
 | 
			
		||||
 | 
			
		||||
            if (dbus_message_has_interface(p->message, "org.bluez.Device")) {
 | 
			
		||||
                if (parse_device_property(y, d, &dict_i) < 0)
 | 
			
		||||
                    goto finish;
 | 
			
		||||
 | 
			
		||||
            } else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) {
 | 
			
		||||
                if (parse_audio_property(y, &d->headset_connected, &dict_i) < 0)
 | 
			
		||||
                    goto finish;
 | 
			
		||||
 | 
			
		||||
            }  else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
 | 
			
		||||
                if (parse_audio_property(y, &d->audio_sink_connected, &dict_i) < 0)
 | 
			
		||||
                    goto finish;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!dbus_message_iter_next(&element_i))
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
finish:
 | 
			
		||||
    run_callback(y, d, TRUE);
 | 
			
		||||
 | 
			
		||||
    dbus_message_unref(r);
 | 
			
		||||
 | 
			
		||||
    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
 | 
			
		||||
    pa_dbus_pending_free(p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessage *m, DBusPendingCallNotifyFunction func) {
 | 
			
		||||
    pa_dbus_pending *p;
 | 
			
		||||
    DBusPendingCall *call;
 | 
			
		||||
 | 
			
		||||
    pa_assert(y);
 | 
			
		||||
    pa_assert(m);
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(dbus_connection_send_with_reply(y->connection, m, &call, -1));
 | 
			
		||||
 | 
			
		||||
    p = pa_dbus_pending_new(m, call, y, d);
 | 
			
		||||
    PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
 | 
			
		||||
    dbus_pending_call_set_notify(call, func, p, NULL);
 | 
			
		||||
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void found_device(pa_bluetooth_discovery *y, const char* path) {
 | 
			
		||||
    DBusMessage *m;
 | 
			
		||||
    pa_bluetooth_device *d;
 | 
			
		||||
 | 
			
		||||
    pa_assert(y);
 | 
			
		||||
    pa_assert(path);
 | 
			
		||||
 | 
			
		||||
    d = device_new(path);
 | 
			
		||||
 | 
			
		||||
    if (y->mode == MODE_DISCOVER) {
 | 
			
		||||
        pa_assert(y->devices);
 | 
			
		||||
        pa_hashmap_put(y->devices, d->path, d);
 | 
			
		||||
    } else {
 | 
			
		||||
        pa_assert(!y->found_device);
 | 
			
		||||
        y->found_device = d;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
 | 
			
		||||
    send_and_add_to_pending(y, d, m, get_properties_reply);
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Headset", "GetProperties"));
 | 
			
		||||
    send_and_add_to_pending(y, d, m, get_properties_reply);
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.AudioSink", "GetProperties"));
 | 
			
		||||
    send_and_add_to_pending(y, d, m, get_properties_reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
 | 
			
		||||
    DBusError e;
 | 
			
		||||
    DBusMessage *r;
 | 
			
		||||
    char **paths = NULL;
 | 
			
		||||
    int num = -1;
 | 
			
		||||
    pa_dbus_pending *p;
 | 
			
		||||
    pa_bluetooth_discovery *y;
 | 
			
		||||
 | 
			
		||||
    pa_assert(pending);
 | 
			
		||||
 | 
			
		||||
    dbus_error_init(&e);
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(p = userdata);
 | 
			
		||||
    pa_assert_se(y = p->context_data);
 | 
			
		||||
    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
 | 
			
		||||
        pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
 | 
			
		||||
        goto end;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
 | 
			
		||||
        pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message);
 | 
			
		||||
        dbus_error_free(&e);
 | 
			
		||||
    } else {
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < num; ++i)
 | 
			
		||||
            found_device(y, paths[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
end:
 | 
			
		||||
    if (paths)
 | 
			
		||||
        dbus_free_string_array (paths);
 | 
			
		||||
 | 
			
		||||
    dbus_message_unref(r);
 | 
			
		||||
 | 
			
		||||
    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
 | 
			
		||||
    pa_dbus_pending_free(p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void find_device_reply(DBusPendingCall *pending, void *userdata) {
 | 
			
		||||
    DBusError e;
 | 
			
		||||
    DBusMessage *r;
 | 
			
		||||
    char *path = NULL;
 | 
			
		||||
    pa_dbus_pending *p;
 | 
			
		||||
    pa_bluetooth_discovery *y;
 | 
			
		||||
 | 
			
		||||
    pa_assert(pending);
 | 
			
		||||
 | 
			
		||||
    dbus_error_init(&e);
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(p = userdata);
 | 
			
		||||
    pa_assert_se(y = p->context_data);
 | 
			
		||||
    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
 | 
			
		||||
        pa_log("Error from FindDevice reply: %s", dbus_message_get_error_name(r));
 | 
			
		||||
        goto end;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!dbus_message_get_args(r, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
 | 
			
		||||
        pa_log("org.bluez.Adapter.FindDevice returned an error: '%s'\n", e.message);
 | 
			
		||||
        dbus_error_free(&e);
 | 
			
		||||
    } else
 | 
			
		||||
        found_device(y, path);
 | 
			
		||||
 | 
			
		||||
end:
 | 
			
		||||
    dbus_message_unref(r);
 | 
			
		||||
 | 
			
		||||
    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
 | 
			
		||||
    pa_dbus_pending_free(p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void found_adapter(pa_bluetooth_discovery *y, const char *path) {
 | 
			
		||||
    DBusMessage *m;
 | 
			
		||||
 | 
			
		||||
    if (y->mode == MODE_FIND) {
 | 
			
		||||
        pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "FindDevice"));
 | 
			
		||||
 | 
			
		||||
        pa_assert_se(dbus_message_append_args(m,
 | 
			
		||||
                                              DBUS_TYPE_STRING, &y->looking_for,
 | 
			
		||||
                                              DBUS_TYPE_INVALID));
 | 
			
		||||
 | 
			
		||||
        send_and_add_to_pending(y, NULL, m, find_device_reply);
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        pa_assert(y->mode == MODE_DISCOVER);
 | 
			
		||||
 | 
			
		||||
        pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "ListDevices"));
 | 
			
		||||
        send_and_add_to_pending(y, NULL, m, list_devices_reply);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
 | 
			
		||||
    DBusError e;
 | 
			
		||||
    DBusMessage *r;
 | 
			
		||||
    char **paths = NULL;
 | 
			
		||||
    int num = -1;
 | 
			
		||||
    pa_dbus_pending *p;
 | 
			
		||||
    pa_bluetooth_discovery *y;
 | 
			
		||||
 | 
			
		||||
    pa_assert(pending);
 | 
			
		||||
 | 
			
		||||
    dbus_error_init(&e);
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(p = userdata);
 | 
			
		||||
    pa_assert_se(y = p->context_data);
 | 
			
		||||
    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
 | 
			
		||||
        pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
 | 
			
		||||
        goto end;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
 | 
			
		||||
        pa_log("org.bluez.Manager.ListAdapters returned an error: %s", e.message);
 | 
			
		||||
        dbus_error_free(&e);
 | 
			
		||||
    } else {
 | 
			
		||||
        int i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < num; ++i)
 | 
			
		||||
            found_adapter(y, paths[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
end:
 | 
			
		||||
    if (paths)
 | 
			
		||||
        dbus_free_string_array (paths);
 | 
			
		||||
 | 
			
		||||
    dbus_message_unref(r);
 | 
			
		||||
 | 
			
		||||
    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
 | 
			
		||||
    pa_dbus_pending_free(p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void list_adapters(pa_bluetooth_discovery *y) {
 | 
			
		||||
    DBusMessage *m;
 | 
			
		||||
    pa_assert(y);
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
 | 
			
		||||
    send_and_add_to_pending(y, NULL, m, list_adapters_reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
 | 
			
		||||
    DBusError err;
 | 
			
		||||
    pa_bluetooth_discovery *y;
 | 
			
		||||
 | 
			
		||||
    pa_assert(bus);
 | 
			
		||||
    pa_assert(m);
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(y = userdata);
 | 
			
		||||
 | 
			
		||||
    dbus_error_init(&err);
 | 
			
		||||
 | 
			
		||||
    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
 | 
			
		||||
            dbus_message_get_interface(m),
 | 
			
		||||
            dbus_message_get_path(m),
 | 
			
		||||
            dbus_message_get_member(m));
 | 
			
		||||
 | 
			
		||||
    if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceRemoved")) {
 | 
			
		||||
        const char *path;
 | 
			
		||||
        pa_bluetooth_device *d;
 | 
			
		||||
 | 
			
		||||
        if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
 | 
			
		||||
            pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err.message);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pa_log_debug("Device %s removed", path);
 | 
			
		||||
 | 
			
		||||
        if ((d = pa_hashmap_remove(y->devices, path))) {
 | 
			
		||||
 | 
			
		||||
            pa_assert_se(y->mode == MODE_DISCOVER);
 | 
			
		||||
            run_callback(y, d, FALSE);
 | 
			
		||||
 | 
			
		||||
            pa_bluetooth_device_free(d);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
 | 
			
		||||
    } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceCreated")) {
 | 
			
		||||
        const char *path;
 | 
			
		||||
 | 
			
		||||
        if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
 | 
			
		||||
            pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err.message);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pa_log_debug("Device %s created", path);
 | 
			
		||||
 | 
			
		||||
        found_device(y, path);
 | 
			
		||||
        return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
 | 
			
		||||
    } else if (dbus_message_is_signal(m, "org.bluez.Manager", "AdapterAdded")) {
 | 
			
		||||
        const char *path;
 | 
			
		||||
 | 
			
		||||
        if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
 | 
			
		||||
            pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err.message);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pa_log_debug("Adapter %s created", path);
 | 
			
		||||
 | 
			
		||||
        found_adapter(y, path);
 | 
			
		||||
        return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
 | 
			
		||||
    } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
 | 
			
		||||
               dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
 | 
			
		||||
               dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
 | 
			
		||||
 | 
			
		||||
        pa_bluetooth_device *d;
 | 
			
		||||
 | 
			
		||||
        if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
 | 
			
		||||
            DBusMessageIter arg_i;
 | 
			
		||||
 | 
			
		||||
            if (!dbus_message_iter_init(m, &arg_i)) {
 | 
			
		||||
                pa_log("Failed to parse PropertyChanged: %s", err.message);
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (dbus_message_has_interface(m, "org.bluez.Device")) {
 | 
			
		||||
                if (parse_device_property(y, d, &arg_i) < 0)
 | 
			
		||||
                    goto fail;
 | 
			
		||||
 | 
			
		||||
            } else if (dbus_message_has_interface(m, "org.bluez.Headset")) {
 | 
			
		||||
                if (parse_audio_property(y, &d->headset_connected, &arg_i) < 0)
 | 
			
		||||
                    goto fail;
 | 
			
		||||
 | 
			
		||||
            }  else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
 | 
			
		||||
                if (parse_audio_property(y, &d->audio_sink_connected, &arg_i) < 0)
 | 
			
		||||
                    goto fail;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pa_assert_se(y->mode == MODE_DISCOVER);
 | 
			
		||||
            run_callback(y, d, TRUE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    dbus_error_free(&err);
 | 
			
		||||
 | 
			
		||||
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address) {
 | 
			
		||||
    pa_bluetooth_discovery y;
 | 
			
		||||
 | 
			
		||||
    memset(&y, 0, sizeof(y));
 | 
			
		||||
    y.mode = MODE_FIND;
 | 
			
		||||
    y.looking_for = address;
 | 
			
		||||
    y.connection = c;
 | 
			
		||||
    PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending);
 | 
			
		||||
 | 
			
		||||
    list_adapters(&y);
 | 
			
		||||
 | 
			
		||||
    pa_dbus_sync_pending_list(&y.pending);
 | 
			
		||||
    pa_assert(!y.pending);
 | 
			
		||||
 | 
			
		||||
    if (y.found_device) {
 | 
			
		||||
        pa_assert(device_is_loaded(y.found_device));
 | 
			
		||||
 | 
			
		||||
        if (!device_is_audio(y.found_device)) {
 | 
			
		||||
            pa_bluetooth_device_free(y.found_device);
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return y.found_device;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path) {
 | 
			
		||||
    pa_bluetooth_discovery y;
 | 
			
		||||
 | 
			
		||||
    memset(&y, 0, sizeof(y));
 | 
			
		||||
    y.mode = MODE_GET;
 | 
			
		||||
    y.connection = c;
 | 
			
		||||
    PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending);
 | 
			
		||||
 | 
			
		||||
    found_device(&y, path);
 | 
			
		||||
 | 
			
		||||
    pa_dbus_sync_pending_list(&y.pending);
 | 
			
		||||
    pa_assert(!y.pending);
 | 
			
		||||
 | 
			
		||||
    if (y.found_device) {
 | 
			
		||||
        pa_assert(device_is_loaded(y.found_device));
 | 
			
		||||
 | 
			
		||||
        if (!device_is_audio(y.found_device)) {
 | 
			
		||||
            pa_bluetooth_device_free(y.found_device);
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return y.found_device;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u) {
 | 
			
		||||
    DBusError err;
 | 
			
		||||
    pa_bluetooth_discovery *y;
 | 
			
		||||
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_assert(cb);
 | 
			
		||||
 | 
			
		||||
    dbus_error_init(&err);
 | 
			
		||||
 | 
			
		||||
    y = pa_xnew0(pa_bluetooth_discovery, 1);
 | 
			
		||||
    y->mode = MODE_DISCOVER;
 | 
			
		||||
    y->connection = c;
 | 
			
		||||
    y->callback = cb;
 | 
			
		||||
    y->userdata = u;
 | 
			
		||||
    y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 | 
			
		||||
    PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
 | 
			
		||||
 | 
			
		||||
    /* dynamic detection of bluetooth audio devices */
 | 
			
		||||
    if (!dbus_connection_add_filter(c, filter_cb, y, NULL)) {
 | 
			
		||||
        pa_log_error("Failed to add filter function");
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pa_dbus_add_matches(
 | 
			
		||||
                c, &err,
 | 
			
		||||
                "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
 | 
			
		||||
                "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
 | 
			
		||||
                "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
 | 
			
		||||
                "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
 | 
			
		||||
                "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
 | 
			
		||||
                "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL) < 0) {
 | 
			
		||||
        pa_log("Failed to add D-Bus matches: %s", err.message);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    list_adapters(y);
 | 
			
		||||
 | 
			
		||||
    return y;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    dbus_error_free(&err);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_bluetooth_discovery_free(pa_bluetooth_discovery *y) {
 | 
			
		||||
    pa_bluetooth_device *d;
 | 
			
		||||
 | 
			
		||||
    pa_assert(y);
 | 
			
		||||
 | 
			
		||||
    pa_dbus_free_pending_list(&y->pending);
 | 
			
		||||
 | 
			
		||||
    if (y->devices) {
 | 
			
		||||
        while ((d = pa_hashmap_steal_first(y->devices))) {
 | 
			
		||||
            run_callback(y, d, FALSE);
 | 
			
		||||
            pa_bluetooth_device_free(d);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pa_hashmap_free(y->devices, NULL, NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (y->connection) {
 | 
			
		||||
        pa_dbus_remove_matches(y->connection,
 | 
			
		||||
                               "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
 | 
			
		||||
                               "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
 | 
			
		||||
                               "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
 | 
			
		||||
                               "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
 | 
			
		||||
                               "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
 | 
			
		||||
                               "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
 | 
			
		||||
                               "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL);
 | 
			
		||||
 | 
			
		||||
        dbus_connection_remove_filter(y->connection, filter_cb, y);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) {
 | 
			
		||||
    pa_assert(y);
 | 
			
		||||
 | 
			
		||||
    pa_dbus_sync_pending_list(&y->pending);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue