diff options
author | Charles Kerr <charles.kerr@canonical.com> | 2013-08-05 16:48:17 -0500 |
---|---|---|
committer | Charles Kerr <charles.kerr@canonical.com> | 2013-08-05 16:48:17 -0500 |
commit | c1ce02f2b8cd198712606888d08b36f1e7aefe39 (patch) | |
tree | 070c84ca09c0041eea98cd4be6be6cc97008470d /src/bluez.vala | |
parent | 9837ce82575d089b52269ff600820d565be3fd22 (diff) | |
download | ayatana-indicator-bluetooth-c1ce02f2b8cd198712606888d08b36f1e7aefe39.tar.gz ayatana-indicator-bluetooth-c1ce02f2b8cd198712606888d08b36f1e7aefe39.tar.bz2 ayatana-indicator-bluetooth-c1ce02f2b8cd198712606888d08b36f1e7aefe39.zip |
fully implement the bluez/device backend. in the desktop profile, add menuitems for the devices.
Diffstat (limited to 'src/bluez.vala')
-rw-r--r-- | src/bluez.vala | 288 |
1 files changed, 249 insertions, 39 deletions
diff --git a/src/bluez.vala b/src/bluez.vala index b0c8761..b4d7836 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -18,90 +18,295 @@ */ /** - * Bluetooth implementaion which uses bluez over dbus + * Bluetooth implementaion which uses org.bluez on DBus */ -public class Bluez: Bluetooth +public class Bluez: KillswitchBluetooth { private org.bluez.Manager manager; private org.bluez.Adapter default_adapter; - private HashTable<string,Bluetooth.Device> devices; + private HashTable<string,org.bluez.Device> path_to_proxy; + private HashTable<string,uint> path_to_id; + private HashTable<uint,string> id_to_path; + private HashTable<uint,Device> id_to_device; + private uint next_device_id = 1; public Bluez (KillSwitch killswitch) { base (killswitch); - string default_adapter_object_path = null; + string adapter_path = null; - this.devices = new HashTable<string,Bluetooth.Device>(str_hash, str_equal); + 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); try { manager = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", "/"); - manager.default_adapter_changed.connect ((object_path) => on_default_adapter_changed (object_path)); - default_adapter_object_path = manager.default_adapter (); + adapter_path = manager.default_adapter (); } catch (Error e) - { - critical ("%s", e.message); - } + { + critical (@"$(e.message)"); + } - on_default_adapter_changed (default_adapter_object_path); + on_default_adapter_changed (adapter_path); } private void on_default_adapter_changed (string? object_path) { if (object_path != null) try { - message ("using default adapter at %s", object_path); + message (@"using default adapter at $object_path"); default_adapter = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_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)); - default_adapter.device_removed.connect((adapter, path) => devices.remove (path)); foreach (string device_path in default_adapter.list_devices()) add_device (device_path); } catch (Error e) { - critical ("%s", e.message); + critical (@"$(e.message)"); } this.on_default_adapter_properties_changed (); } - private void add_device (string object_path) + private static uint16 get_uuid16_from_uuid_string (string uuid) + { + uint16 uuid16; + + string[] tokens = uuid.split ("-", 1); + if (tokens.length > 0) + uuid16 = (uint16) uint64.parse ("0x"+tokens[0]); + else + uuid16 = 0; + + return uuid16; + } + + /* A device supports file transfer if OBEXObjectPush is in its uuid list */ + private bool device_supports_file_transfer (uint16[] uuids) + { + foreach (uint16 uuid16 in uuids) + if (uuid16 == 0x1105) // OBEXObjectPush + return true; + + return false; + } + + /* A device supports browsing if OBEXFileTransfer is in its uuid list */ + private bool device_supports_browsing (uint16[] uuids) { + foreach (uint16 uuid16 in uuids) + if (uuid16 == 0x1106) // OBEXFileTransfer + return true; + + return false; + } + + /* headsets, audio sinks, and input devices are connectable. + * + * TODO: this duplicates the behavior of the indicator from when it used + * gnome-bluetooth as a backend. Are there other interfaces we care about? */ + private DBusInterfaceInfo[] get_connectable_interfaces (DBusProxy device) + { + DBusInterfaceInfo[] connectable_interfaces = {}; + try { - org.bluez.Device device = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", object_path); - message ("got device proxy for %s", object_path); - var properties = device.get_properties (); + var iname = "org.freedesktop.DBus.Introspectable.Introspect"; + var intro = device.call_sync (iname, null, DBusCallFlags.NONE, -1); - Variant v = properties.lookup ("Alias"); - if (v == null) - v = properties.lookup ("Name"); - string name = v == null ? _("Unknown") : v.get_string(); + if ((intro != null) && (intro.n_children() > 0)) + { + string xml = intro.get_child_value(0).get_string(); + var info = new DBusNodeInfo.for_xml (xml); + if (info != null) + { + foreach (DBusInterfaceInfo 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)"); + } - bool supports_browsing = false; - v = properties.lookup ("UUIDs"); - message ("%s", v.print(true)); + return connectable_interfaces; + } - bool supports_file_transfer = false; + private bool device_is_connectable (DBusProxy device) + { + var connectable_interfaces = get_connectable_interfaces (device); + return connectable_interfaces.length > 0; + } - //protected static bool uuid_supports_file_transfer (string uuid) - //protected static bool uuid_supports_browsing (string uuid) + private void device_connect (DBusProxy proxy) + { + var connection = proxy.get_connection (); + var object_path = proxy.get_object_path (); - var dev = new Bluetooth.Device (name, - supports_browsing, - supports_file_transfer); - devices.insert (object_path, dev); - message ("devices.size() is %u", devices.size()); + foreach (var i in get_connectable_interfaces (proxy)) + { + try + { + debug (@"trying to connect to $object_path: $(i.name)"); + connection.call_sync ("org.bluez", + object_path, + i.name, + "Connect", + null, + null, + DBusCallFlags.NONE, + -1); + } + catch (Error e) + { + debug (@"Unable to call $(i.name).Connect() on $(proxy.get_object_path()): $(e.message)"); + } + } + } + + 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)"); + } + } + } + + private void update_device (org.bluez.Device device_proxy) + { + HashTable<string, GLib.Variant> properties; + + try { + properties = device_proxy.get_properties (); + } catch (Error e) { + critical (@"$(e.message)"); + return; + } + + // 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) + { + id = next_device_id ++; + id_to_path.insert (id, object_path); + path_to_id.insert (object_path, id); + } + + // look up the device's type + Device.Type type; + var v = properties.lookup ("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"); + if (v == null) + v = properties.lookup ("Name"); + string name = v == null ? _("Unknown") : v.get_string (); + + // look up the device's bus address + v = properties.lookup ("Address"); + string address = v.get_string (); + + // look up the device's bus address + Icon icon; + v = properties.lookup ("Icon"); + if (v == null) + icon = new ThemedIcon ("unknown"); + else + icon = new ThemedIcon (v.get_string()); + + // 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"); + bool is_connected = (v != null) && v.get_boolean (); + + // derive the uuid-related attributes we care about + v = properties.lookup ("UUIDs"); + string[] uuid_strings = v.dup_strv (); + uint16[] uuids = {}; + foreach (string s in uuid_strings) + uuids += get_uuid16_from_uuid_string (s); + 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, + is_connected, + supports_browsing, + supports_file_transfer)); + + devices_changed (); + } + + public override void set_device_connected (uint id, bool connected) + { + var device = id_to_device.lookup (id); + var object_path = id_to_path.lookup (id); + var proxy = (object_path != null) ? path_to_proxy.lookup (object_path) : null; + + if ((proxy != null) && (device != null) && (device.is_connected != connected)) + { + if (connected) + { + device_connect (proxy as DBusProxy); + } + else // disconnect + { + try + { + proxy.disconnect (); + } + catch (Error e) + { + critical (@"Unable to disconnect $object_path: $(e.message)"); + } + } } - catch (Error e) - { - critical ("%s", e.message); - } } private void on_default_adapter_properties_changed () @@ -121,7 +326,7 @@ public class Bluez: Bluetooth } catch (Error e) { - critical ("%s", e.message); + critical (@"$(e.message)"); } this.powered = is_powered; @@ -136,7 +341,12 @@ public class Bluez: Bluetooth } catch (Error e) { - critical ("%s", e.message); + critical (@"$(e.message)"); } } + + public override List<unowned Device> get_devices () + { + return id_to_device.get_values(); + } } |