/** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program 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 General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * AUTHORS * Maciek Borzecki <maciek.borzecki (at] gmail.com> * gyan000 <gyan000 (at] ijaz.fr> */ using Gee; using EOSConnect; namespace MConnect { public class DeviceManager : GLib.Object { public signal void found_new_device (Device dev); public signal void device_connected (Device dev); public signal void device_deconnected (Device dev); public signal void device_pair_changed (Device dev); public signal void device_capability_added (Device dev, string capability, PacketHandlerInterface handler); public const string DEVICES_CACHE_FILE = "devices"; public DeviceManager () { debug ("Device manager."); var notification_monitor = new NotificationMonitor (); notification_monitor.notification_received.connect (on_local_notification_received); notification_monitor.notification_closed.connect (on_local_notification_closed); } /** * Obtain path to devices cache file */ private string get_cache_file () { var cache_file = Path.build_filename (Core.get_cache_dir (), DEVICES_CACHE_FILE); debug ("Cache file: %s", cache_file); // make sure that cache dir exists DirUtils.create_with_parents (Core.get_cache_dir (), 0700); return cache_file; } /** * Load known devices from cache and attempt pairing. */ public void load_cache () { var cache_file = this.get_cache_file (); debug ("Try loading devices from device cache %s", cache_file); var kf = new KeyFile (); try { kf.load_from_file (cache_file, KeyFileFlags.NONE); string[] groups = kf.get_groups (); foreach (string group in groups) { var dev = Device.new_from_cache (kf, group); if (dev != null) { debug ("device %s from cache", dev.to_string ()); handle_device (dev); } } } catch (Error e) { debug ("Error loading cache file: %s", e.message); } } public void handle_discovered_device (DiscoveredDevice discovered_dev) { debug ("Found device: %s", discovered_dev.to_string ()); var new_dev = new Device.from_discovered_device (discovered_dev); handle_device (new_dev); } public void handle_device (Device new_dev) { try { var is_new = false; string unique = new_dev.to_unique_string (); debug ("Device key: %s", unique); if (Core.instance ().devices_map.has_key (unique) == false) { debug ("Adding new device with key: %s", unique); Core.instance ().devices_map.@set (unique, new_dev); is_new = true; } else { debug ("Device %s already present", unique); this.device_connected (Core.instance ().devices_map.@get (unique)); } var dev = Core.instance ().devices_map.@get (unique); // Notify everyone that a new device appeared if (is_new) { // Make sure that this happens before we update device data so that // all subscribeds of found_new_device() signal have a chance to // setup eveything they need. this.found_new_device (dev); dev.capability_added.connect (this.device_capability_added_cb); dev.capability_removed.connect (this.device_capability_removed_cb); } // Update device information dev.update_from_device (new_dev); debug ("Allowed? %s", dev.allowed.to_string ()); // check if device is whitelisted in configuration if (!dev.allowed && device_allowed_in_config (dev)) { dev.allowed = true; } this.update_cache (); if (dev.allowed) { this.activate_device (dev); } else { warning ("Skipping device %s activation, device not allowed", dev.to_string ()); } } catch (Error e) { warning ("Error: %s", e.message); } } /** * allow_device: * @path: device object path * * Allow given device */ public void allow_device (Device dev) { dev.allowed = true; update_cache (); activate_device (dev); } /** * disallow_device: * @path: device object path * * Disallow given device */ public void disallow_device (Device dev) { dev.allowed = false; update_cache (); } private void on_local_notification_received (DBusMessage message, uint32 id) { try { var notification = new Notification.from_message (message, id); if (!notification.get_is_valid () || notification.app_name in Notification.EXCEPTIONS) { return; } foreach (Device device in Core.instance ().devices_map.values) { if (device.has_capability_handler (NotificationHandler.NOTIFICATION)) { NotificationHandler notification_handler = (NotificationHandler)device.get_path_capability_handler( NotificationHandler.NOTIFICATION); notification_handler.send_notification (device, message, id); } } } catch (Error e) { warning ("Error: %s", e.message); } } private void on_local_notification_closed (Notification notification) { try { print ("\n>>%S\n", notification.id); // var notification = new Notification.from_message (message, id); // Core.instance ().application.withdraw_notification (notification_id); // if (!notification.get_is_valid () || notification.app_name in Notification.EXCEPTIONS) { // return; // } // // foreach (Device device in Core.instance ().devices_map.values) { // if (device.has_capability_handler (NotificationHandler.NOTIFICATION)) { // NotificationHandler notification_handler = (NotificationHandler)device.get_path_capability_handler( // NotificationHandler.NOTIFICATION); // // notification_handler.send_notification (device, message, id); // } // } } catch (Error e) { warning ("Error: %s", e.message); } } /** * Update contents of device cache */ private void update_cache () { try { if (Core.instance ().devices_map.size == 0) return; var kf = new KeyFile (); foreach (var dev in Core.instance ().devices_map.values) { dev.to_cache (kf, dev.name); } debug("Saving to cache."); FileUtils.set_contents (get_cache_file (), kf.to_data ()); } catch (FileError e) { warning ("Failed to save to cache file %s: %s", get_cache_file (), e.message); } catch (Error e) { warning ("Error: %s", e.message); } } private void activate_device (Device dev) { info ("Activating device %s, active: %s", dev.to_string (), dev.is_active.to_string ()); if (!dev.is_active) { dev.paired.connect (this.device_paired); dev.disconnected.connect (this.device_disconnected); dev.activate (); } } /** * device_allowed_in_config: * @dev device * * Returns true if a matching device is enabled via configuration file. */ private bool device_allowed_in_config (Device dev) throws Error { if (dev.allowed) return true; // var core = Core.instance (); // var in_config = core.config.is_device_allowed (dev.name, dev.device_type); // return in_config; return true; } private void device_paired (Device dev, bool status) { info ("Device %s pair status change: %s", dev.to_string (), status.to_string ()); update_cache (); if (status == false) { // we're no longer interested in paired signal dev.paired.disconnect (this.device_paired); // we're not paired anymore, deactivate if needed dev.deactivate (); } device_pair_changed (dev); } private void device_capability_added_cb (Device dev, string cap) { try { info ("Capability %s added to device %s", cap, dev.to_string ()); if (dev.has_capability_handler (cap)) { return; } var core = Core.instance (); var h = core.handlers.get_capability_handler (cap); if (h != null) { dev.register_capability_handler (cap, h); device_capability_added (dev, cap, h); } else { warning ("No handler for capability %s", cap); } } catch (Error e) { warning ("Error: %s", e.message); } } private void device_capability_removed_cb (Device dev, string cap) { info ("Capability %s removed from device %s", cap, dev.to_string ()); } private void device_disconnected (Device dev) { debug ("Device %s got disconnected", dev.to_string ()); dev.paired.disconnect (this.device_paired); dev.disconnected.disconnect (this.device_disconnected); device_deconnected (dev); } } }