diff options
author | Robert Ancell <robert.ancell@canonical.com> | 2014-12-11 15:50:16 +1300 |
---|---|---|
committer | Robert Ancell <robert.ancell@canonical.com> | 2014-12-11 15:50:16 +1300 |
commit | 682e6149ef26407e6fb7b55ebf6cc74b55f45960 (patch) | |
tree | 425119abadf174dcbd3f505df3bb2fc7926c0524 /src/bluez.vala | |
parent | b60855a8fcd07434e927995af449d58671dac96b (diff) | |
download | ayatana-indicator-bluetooth-682e6149ef26407e6fb7b55ebf6cc74b55f45960.tar.gz ayatana-indicator-bluetooth-682e6149ef26407e6fb7b55ebf6cc74b55f45960.tar.bz2 ayatana-indicator-bluetooth-682e6149ef26407e6fb7b55ebf6cc74b55f45960.zip |
Support Bluez 5
When enabling/disabling and setting discoverability we now do this for all
adapters.
This requires us to remove the "Set Up New Device" item since gnome-bluetooth no
longer has a bluetooth wizard stand alone application. Instead we rely on the
unity-control-center panel for device setup.
Diffstat (limited to 'src/bluez.vala')
-rw-r--r-- | src/bluez.vala | 408 |
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; +} |