aboutsummaryrefslogtreecommitdiff
path: root/src/bluez.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluez.vala')
-rw-r--r--src/bluez.vala408
1 files changed, 173 insertions, 235 deletions
diff --git a/src/bluez.vala b/src/bluez.vala
index 323674c..2dc2b04 100644
--- a/src/bluez.vala
+++ b/src/bluez.vala
@@ -25,8 +25,7 @@
public class Bluez: Bluetooth, Object
{
uint next_device_id = 1;
- org.bluez.Manager manager;
- org.bluez.Adapter default_adapter;
+ ObjectManager manager;
private bool _powered = false;
@@ -37,21 +36,22 @@ public class Bluez: Bluetooth, Object
private KillSwitch killswitch = new RfKillSwitch ();
- private string adapter_path = null;
-
private DBusConnection bus = null;
- /* maps an org.bluez.Device's object_path to the org.bluez.Device proxy */
- HashTable<string,org.bluez.Device> path_to_proxy;
+ /* maps an org.bluez.Adapter1's object_path to the BluezAdapter proxy */
+ private HashTable<ObjectPath,BluezAdapter> path_to_adapter_proxy;
+
+ /* maps an org.bluez.Device1's object_path to the BluezDevice proxy */
+ private HashTable<ObjectPath,BluezDevice> path_to_device_proxy;
- /* maps an org.bluez.Device's object_path to our arbitrary unique id */
- HashTable<string,uint> path_to_id;
+ /* maps an org.bluez.Device1's object_path to our arbitrary unique id */
+ private HashTable<ObjectPath,uint> path_to_id;
/* maps our arbitrary unique id to an org.bluez.Device's object path */
- HashTable<uint,string> id_to_path;
+ private HashTable<uint,ObjectPath> id_to_path;
/* maps our arbitrary unique id to a Bluetooth.Device struct for public consumption */
- HashTable<uint,Device> id_to_device;
+ private HashTable<uint,Device> id_to_device;
public Bluez (KillSwitch? killswitch)
{
@@ -71,121 +71,116 @@ public class Bluez: Bluetooth, Object
update_enabled ();
}
- id_to_path = new HashTable<uint,string> (direct_hash, direct_equal);
- id_to_device = new HashTable<uint,Device> (direct_hash, direct_equal);
- path_to_id = new HashTable<string,uint> (str_hash, str_equal);
- path_to_proxy = new HashTable<string,org.bluez.Device> (str_hash, str_equal);
-
reset_manager ();
}
private void reset_manager ()
{
- string new_adapter_path = null;
+ id_to_path = new HashTable<uint,ObjectPath> (direct_hash, direct_equal);
+ id_to_device = new HashTable<uint,Device> (direct_hash, direct_equal);
+ path_to_id = new HashTable<ObjectPath,uint> (str_hash, str_equal);
+ path_to_adapter_proxy = new HashTable<ObjectPath,BluezAdapter> (str_hash, str_equal);
+ path_to_device_proxy = new HashTable<ObjectPath,BluezDevice> (str_hash, str_equal);
+
try
{
manager = bus.get_proxy_sync ("org.bluez", "/");
- // if the default adapter changes, update our connections
- manager.default_adapter_changed.connect ((object_path)
- => on_default_adapter_changed (object_path));
-
- // if the current adapter disappears, call clear_adapter()
- manager.adapter_removed.connect ((object_path) => {
- if (object_path == adapter_path)
- clear_adapter ();
+ // Find the adapters and watch for changes
+ manager.interfaces_added.connect ((object_path, interfaces_and_properties) => {
+ var iter = HashTableIter<string, HashTable<string, Variant>> (interfaces_and_properties);
+ string name;
+ while (iter.next (out name, null))
+ {
+ if (name == "org.bluez.Adapter1")
+ update_adapter (object_path);
+ if (name == "org.bluez.Device1")
+ update_device (object_path);
+ }
});
-
- // get the current default adapter & watch for future default adapters
- new_adapter_path = manager.default_adapter ();
+ manager.interfaces_removed.connect ((object_path, interfaces) => {
+ foreach (var interface in interfaces) {
+ if (interface == "org.bluez.Adapter1")
+ adapter_removed (object_path);
+ if (interface == "org.bluez.Device1")
+ device_removed (object_path);
+ }
+ });
+ var objects = manager.get_managed_objects ();
+ var object_iter = HashTableIter<ObjectPath, HashTable<string, HashTable<string, Variant>>> (objects);
+ ObjectPath object_path;
+ HashTable<string, HashTable<string, Variant>> interfaces_and_properties;
+ while (object_iter.next (out object_path, out interfaces_and_properties))
+ {
+ var iter = HashTableIter<string, HashTable<string, Variant>> (interfaces_and_properties);
+ string name;
+ while (iter.next (out name, null)) {
+ if (name == "org.bluez.Adapter1")
+ update_adapter (object_path);
+ if (name == "org.bluez.Device1")
+ update_device (object_path);
+ }
+ }
}
catch (Error e)
{
critical (@"$(e.message)");
}
-
- on_default_adapter_changed (new_adapter_path);
}
- private void clear_adapter ()
- {
- if (adapter_path != null)
- debug (@"clearing adapter; was using $adapter_path");
-
- path_to_proxy.remove_all ();
- path_to_id.remove_all ();
- id_to_path.remove_all ();
- id_to_device.remove_all ();
-
- default_adapter = null;
- adapter_path = null;
-
- discoverable = false;
- powered = false;
- }
+ ////
+ //// Adapter Upkeep
+ ////
- void on_default_adapter_changed (string? object_path)
+ private void update_adapter (ObjectPath object_path)
{
- clear_adapter ();
-
- if (object_path != null) try
+ // Create a proxy if we don't have one
+ var adapter_proxy = path_to_adapter_proxy.lookup (object_path);
+ if (adapter_proxy == null)
{
- adapter_path = object_path;
- default_adapter = Bus.get_proxy_sync (BusType.SYSTEM,
- "org.bluez",
- adapter_path);
-
- default_adapter.property_changed.connect (()
- => on_default_adapter_properties_changed ());
-
- default_adapter.device_removed.connect ((adapter, path) => {
- var id = path_to_id.lookup (path);
- path_to_id.remove (path);
- id_to_path.remove (id);
- id_to_device.remove (id);
- devices_changed ();
- });
-
- default_adapter.device_created.connect ((adapter, path)
- => add_device (path));
-
- foreach (var device_path in default_adapter.list_devices ())
- add_device (device_path);
+ try {
+ adapter_proxy = bus.get_proxy_sync ("org.bluez", object_path);
+ } catch (Error e) {
+ critical (@"$(e.message)");
+ return;
+ }
+ path_to_adapter_proxy.insert (object_path, adapter_proxy);
+ adapter_proxy.g_properties_changed.connect(() => update_adapter (object_path));
}
- catch (Error e)
- {
- critical (@"$(e.message)");
- }
- supported = object_path != null;
+ update_combined_adapter_state ();
+ }
- on_default_adapter_properties_changed ();
+ private void adapter_removed (ObjectPath object_path)
+ {
+ path_to_adapter_proxy.remove (object_path);
+ update_combined_adapter_state ();
}
- /* When the default adapter's properties change,
- update our own properties "powered" and "discoverable" */
- private void on_default_adapter_properties_changed ()
+ private void update_combined_adapter_state ()
{
- bool is_discoverable = false;
- bool is_powered = false;
+ var is_discoverable = false;
+ var is_powered = false;
+ var is_supported = false;
- if (default_adapter != null) try
+ var iter = HashTableIter<ObjectPath,BluezAdapter> (path_to_adapter_proxy);
+ BluezAdapter adapter_proxy;
+ while (iter.next (null, out adapter_proxy))
{
- var properties = default_adapter.get_properties ();
+ var v = adapter_proxy.get_cached_property ("Discoverable");
+ if (!is_discoverable)
+ is_discoverable = (v != null) && v.get_boolean ();
- var v = properties.lookup ("Discoverable");
- is_discoverable = (v != null) && v.get_boolean ();
+ v = adapter_proxy.get_cached_property ("Powered");
+ if (!is_powered)
+ is_powered = (v != null) && v.get_boolean ();
- v = properties.lookup ("Powered");
- is_powered = (v != null) && v.get_boolean ();
+ is_supported = true;
}
- catch (Error e)
- {
- critical (@"$(e.message)");
- }
- powered = is_powered;
discoverable = is_discoverable;
+ powered = is_powered;
+ supported = is_supported;
}
////
@@ -226,138 +221,34 @@ public class Bluez: Bluetooth, Object
}
////
- //// Connectable Interfaces
- ////
-
- /* Headsets, Audio Sinks, and Input devices are connectable.
- *
- * This continues the behavior of the old gnome-bluetooth indicator.
- * But are there other interfaces we care about? */
- private DBusInterfaceInfo[] get_connectable_interfaces (DBusProxy device)
- {
- DBusInterfaceInfo[] connectable_interfaces = {};
-
- try
- {
- var iname = "org.freedesktop.DBus.Introspectable.Introspect";
- var intro = device.call_sync (iname, null, DBusCallFlags.NONE, -1);
-
- if ((intro != null) && (intro.n_children() > 0))
- {
- var xml = intro.get_child_value(0).get_string();
- var info = new DBusNodeInfo.for_xml (xml);
- if (info != null)
- {
- foreach (var i in info.interfaces)
- {
- if ((i.name == "org.bluez.AudioSink") ||
- (i.name == "org.bluez.Headset") ||
- (i.name == "org.bluez.Input"))
- {
- connectable_interfaces += i;
- }
- }
- }
- }
- }
- catch (Error e)
- {
- critical (@"$(e.message)");
- }
-
- return connectable_interfaces;
- }
-
- private bool device_is_connectable (DBusProxy device)
- {
- return get_connectable_interfaces (device).length > 0;
- }
-
- // call "Connect" on the specified interface
- private void device_connect_on_interface (DBusProxy proxy,
- string interface_name)
- {
- var object_path = proxy.get_object_path ();
-
- debug (@"trying to connect to $object_path: $(interface_name)");
-
- try
- {
- bus.call_sync ("org.bluez", object_path, interface_name,
- "Connect", null, null, DBusCallFlags.NONE, -1);
- }
- catch (Error e)
- {
- debug (@"$object_path $interface_name.Connect() failed: $(e.message)");
- }
- }
-
- private void device_connect (org.bluez.Device device)
- {
- DBusProxy proxy = device as DBusProxy;
-
- // call "Connect" on all the interfaces that support it
- foreach (var i in get_connectable_interfaces (proxy))
- device_connect_on_interface (proxy, i.name);
- }
-
- private void device_disconnect (org.bluez.Device device)
- {
- try
- {
- device.disconnect ();
- }
- catch (Error e)
- {
- var object_path = (device as DBusProxy).get_object_path ();
- critical (@"Unable to disconnect $object_path: $(e.message)");
- }
- }
-
- ////
//// Device Upkeep
////
- private void add_device (string object_path)
- {
- if (!path_to_proxy.contains (object_path))
- {
- try
- {
- org.bluez.Device device = Bus.get_proxy_sync (BusType.SYSTEM,
- "org.bluez",
- object_path);
- path_to_proxy.insert (object_path, device);
- device.property_changed.connect(() => update_device (device));
- update_device (device);
- }
- catch (Error e)
- {
- critical (@"$(e.message)");
- }
- }
- }
-
/* Update our public Device struct from the org.bluez.Device's properties.
*
* This is called when we first walk through bluez' Devices on startup,
* when the org.bluez.Adapter gets a new device,
* and when a device's properties change s.t. we need to rebuild the proxy.
*/
- private void update_device (org.bluez.Device device_proxy)
+ private void update_device (ObjectPath object_path)
{
- HashTable<string, GLib.Variant> properties;
-
- try {
- properties = device_proxy.get_properties ();
- } catch (Error e) {
- critical (@"$(e.message)");
- return;
- }
+ // Create a proxy if we don't have one
+ var device_proxy = path_to_device_proxy.lookup (object_path);
+ if (device_proxy == null)
+ {
+ try {
+ device_proxy = bus.get_proxy_sync ("org.bluez", object_path);
+ } catch (Error e) {
+ critical (@"$(e.message)");
+ return;
+ }
+
+ path_to_device_proxy.insert (object_path, device_proxy);
+ device_proxy.g_properties_changed.connect(() => update_device (object_path));
+ }
// look up our id for this device.
// if we don't have one yet, create one.
- var object_path = (device_proxy as DBusProxy).get_object_path();
var id = path_to_id.lookup (object_path);
if (id == 0)
{
@@ -368,35 +259,32 @@ public class Bluez: Bluetooth, Object
// look up the device's type
Device.Type type;
- var v = properties.lookup ("Class");
+ var v = device_proxy.get_cached_property ("Class");
if (v == null)
type = Device.Type.OTHER;
else
type = Device.class_to_device_type (v.get_uint32());
// look up the device's human-readable name
- v = properties.lookup ("Alias");
+ v = device_proxy.get_cached_property ("Alias");
if (v == null)
- v = properties.lookup ("Name");
+ v = device_proxy.get_cached_property ("Name");
var name = v == null ? _("Unknown") : v.get_string ();
// look up the device's bus address
- v = properties.lookup ("Address");
+ v = device_proxy.get_cached_property ("Address");
var address = v.get_string ();
// look up the device's bus address
- v = properties.lookup ("Icon");
+ v = device_proxy.get_cached_property ("Icon");
var icon = new ThemedIcon (v != null ? v.get_string() : "unknown");
- // derive a Connectable flag for this device
- var is_connectable = device_is_connectable (device_proxy as DBusProxy);
-
// look up the device's Connected flag
- v = properties.lookup ("Connected");
+ v = device_proxy.get_cached_property ("Connected");
var is_connected = (v != null) && v.get_boolean ();
// derive the uuid-related attributes we care about
- v = properties.lookup ("UUIDs");
+ v = device_proxy.get_cached_property ("UUIDs");
string[] uuid_strings = v.dup_strv ();
uint16[] uuids = {};
foreach (var s in uuid_strings)
@@ -404,13 +292,12 @@ public class Bluez: Bluetooth, Object
var supports_browsing = device_supports_browsing (uuids);
var supports_file_transfer = device_supports_file_transfer (uuids);
- // update our lookup table with these new attributes
id_to_device.insert (id, new Device (id,
type,
name,
address,
icon,
- is_connectable,
+ true,
is_connected,
supports_browsing,
supports_file_transfer));
@@ -419,6 +306,16 @@ public class Bluez: Bluetooth, Object
update_connected ();
}
+ private void device_removed (ObjectPath path)
+ {
+ var id = path_to_id.lookup (path);
+ path_to_id.remove (path);
+ id_to_path.remove (id);
+ id_to_device.remove (id);
+
+ devices_changed ();
+ }
+
/* update the 'enabled' property by looking at the killswitch state
and the 'powered' property state */
void update_enabled ()
@@ -452,16 +349,22 @@ public class Bluez: Bluetooth, Object
{
var device = id_to_device.lookup (id);
var path = id_to_path.lookup (id);
- var proxy = (path != null) ? path_to_proxy.lookup (path) : null;
+ var proxy = (path != null) ? path_to_device_proxy.lookup (path) : null;
- if ((proxy != null)
- && (device != null)
+ if ((device != null)
&& (device.is_connected != connected))
{
- if (connected)
- device_connect (proxy);
- else
- device_disconnect (proxy);
+ try
+ {
+ if (connected)
+ proxy.connect_ ();
+ else
+ proxy.disconnect_ ();
+ }
+ catch (Error e)
+ {
+ debug (@"$path org.bluez.Device1 connect/disconnect failed: $(e.message)");
+ }
update_connected ();
}
@@ -471,7 +374,13 @@ public class Bluez: Bluetooth, Object
{
if (discoverable != b)
{
- default_adapter.set_property.begin ("Discoverable", new Variant.boolean (b));
+ var iter = HashTableIter<ObjectPath,BluezAdapter> (path_to_adapter_proxy);
+ ObjectPath object_path;
+ BluezAdapter adapter_proxy;
+ while (iter.next (out object_path, out adapter_proxy))
+ adapter_proxy.call.begin ("org.freedesktop.DBus.Properties.Set",
+ new Variant ("(ssv)", "org.bluez.Adapter1", "Discoverable", new Variant.boolean (b)),
+ DBusCallFlags.NONE, -1);
}
}
@@ -492,11 +401,40 @@ public class Bluez: Bluetooth, Object
debug (@"setting killswitch blocked to $(!b)");
killswitch.try_set_blocked (!b);
}
- else if (default_adapter != null)
+ else
{
- debug (@"setting bluez Adapter's Powered property to $b");
- default_adapter.set_property.begin ("Powered", new Variant.boolean (b));
- powered = b;
+ var iter = HashTableIter<ObjectPath,BluezAdapter> (path_to_adapter_proxy);
+ ObjectPath object_path;
+ BluezAdapter adapter_proxy;
+ while (iter.next (out object_path, out adapter_proxy))
+ adapter_proxy.call.begin ("org.freedesktop.DBus.Properties.Set",
+ new Variant ("(ssv)", "org.bluez.Adapter1", "Powered", new Variant.boolean (b)),
+ DBusCallFlags.NONE, -1);
}
}
}
+
+[DBus (name = "org.freedesktop.DBus.ObjectManager", timeout = 120000)]
+private interface ObjectManager : Object {
+ [DBus (name = "GetManagedObjects")]
+ public abstract HashTable<ObjectPath, HashTable<string, HashTable<string, Variant>>> get_managed_objects() throws DBusError, IOError;
+
+ [DBus (name = "InterfacesAdded")]
+ public signal void interfaces_added(ObjectPath object_path, HashTable<string, HashTable<string, Variant>> interfaces_and_properties);
+
+ [DBus (name = "InterfacesRemoved")]
+ public signal void interfaces_removed(ObjectPath object_path, string[] interfaces);
+}
+
+[DBus (name = "org.bluez.Adapter1", timeout = 120000)]
+private interface BluezAdapter : DBusProxy {
+}
+
+[DBus (name = "org.bluez.Device1", timeout = 120000)]
+private interface BluezDevice : DBusProxy {
+ [DBus (name = "Connect")]
+ public abstract void connect_() throws DBusError, IOError;
+
+ [DBus (name = "Disconnect")]
+ public abstract void disconnect_() throws DBusError, IOError;
+}