diff options
-rw-r--r-- | src/jarabe/desktop/meshbox.py | 384 | ||||
-rw-r--r-- | src/jarabe/model/neighborhood.py | 77 |
2 files changed, 210 insertions, 251 deletions
diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py index 31dc1e0..1c7dcff 100644 --- a/src/jarabe/desktop/meshbox.py +++ b/src/jarabe/desktop/meshbox.py @@ -16,14 +16,17 @@ from gettext import gettext as _ import logging -import gconf +import sha +import dbus import hippo import gobject +import gconf import gtk from sugar.graphics.icon import CanvasIcon, Icon from sugar.graphics.xocolor import XoColor +from sugar.graphics import xocolor from sugar.graphics import style from sugar.graphics.icon import get_icon_state from sugar.graphics import palette @@ -43,69 +46,138 @@ from jarabe.model import bundleregistry from jarabe.model.network import NM_802_11_CAP_PROTO_WEP, \ NM_802_11_CAP_PROTO_WPA, NM_802_11_CAP_PROTO_WPA2 +_NM_SERVICE = 'org.freedesktop.NetworkManager' +_NM_IFACE = 'org.freedesktop.NetworkManager' +_NM_PATH = '/org/freedesktop/NetworkManager' +_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device' +_NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless' +_NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint' + +_DEVICE_TYPE_802_11_WIRELESS = 2 + +_DEVICE_STATE_UNKNOWN = 0 +_DEVICE_STATE_UNMANAGED = 1 +_DEVICE_STATE_UNAVAILABLE = 2 +_DEVICE_STATE_DISCONNECTED = 3 +_DEVICE_STATE_PREPARE = 4 +_DEVICE_STATE_CONFIG = 5 +_DEVICE_STATE_NEED_AUTH = 6 +_DEVICE_STATE_IP_CONFIG = 7 +_DEVICE_STATE_ACTIVATED = 8 +_DEVICE_STATE_FAILED = 9 _ICON_NAME = 'network-wireless' class AccessPointView(CanvasPulsingIcon): - def __init__(self, model, mesh_device=None): + def __init__(self, device, model): CanvasPulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE, cache=True) + self._bus = dbus.SystemBus() + self._device = device self._model = model - self._meshdev = mesh_device self._disconnect_item = None self._connect_item = None self._greyed_out = False + self._name = '' + self._strength = 0 + self._caps = 0 + self._state = None self.connect('activated', self._activate_cb) - model.connect('notify::strength', self._strength_changed_cb) - model.connect('notify::name', self._name_changed_cb) - model.connect('notify::state', self._state_changed_cb) - pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(), style.COLOR_TRANSPARENT.get_svg())) self.props.pulse_color = pulse_color - # Update badge - caps = model.props.capabilities - if model.get_nm_network().is_favorite(): - self.props.badge_name = "emblem-favorite" - elif (caps & NM_802_11_CAP_PROTO_WEP) or \ - (caps & NM_802_11_CAP_PROTO_WPA) or \ - (caps & NM_802_11_CAP_PROTO_WPA2): - self.props.badge_name = "emblem-locked" - self._palette = self._create_palette() self.set_palette(self._palette) + model_props = dbus.Interface(model, 'org.freedesktop.DBus.Properties') + model_props.GetAll(_NM_ACCESSPOINT_IFACE, byte_arrays=True, + reply_handler=self.__get_all_props_reply_cb, + error_handler=self.__get_all_props_error_cb) + + self._bus.add_signal_receiver(self.__properties_changed_cb, + signal_name='PropertiesChanged', + path=device.object_path, + dbus_interface=_NM_ACCESSPOINT_IFACE) + + self._device.Get(_NM_DEVICE_IFACE, 'State', + reply_handler=self.__get_device_state_reply_cb, + error_handler=self.__get_device_state_error_cb) + + self._bus.add_signal_receiver(self.__state_changed_cb, + signal_name='StateChanged', + path=device.object_path, + dbus_interface=_NM_DEVICE_IFACE) + + def __state_changed_cb(self, state): + self._state = state + self._update() + + def __properties_changed_cb(self, properties): + self._update_properties(properties) + + def _update_properties(self, props): + self._name = props['Ssid'] + self._strength = props['Strength'] + self._caps = props['Flags'] + + self._update() + + def _compute_color(self): + sh = sha.new() + data = self._name + hex(self._caps) + sh.update(data) + h = hash(sh.digest()) + idx = h % len(xocolor.colors) + + # stroke, fill + return (xocolor.colors[idx][0], xocolor.colors[idx][1]) + + def __get_device_state_reply_cb(self, state): + self._state = state + self._update() + + def __get_device_state_error_cb(self, err): + logging.debug('Error getting the access point properties: %s', err) + + def __get_all_props_reply_cb(self, properties): + self._update_properties(properties) + + def __get_all_props_error_cb(self, err): + logging.debug('Error getting the access point properties: %s', err) + + def _update(self): + if False: + self.props.badge_name = "emblem-favorite" + elif (self._caps & NM_802_11_CAP_PROTO_WEP) or \ + (self._caps & NM_802_11_CAP_PROTO_WPA) or \ + (self._caps & NM_802_11_CAP_PROTO_WPA2): + self.props.badge_name = "emblem-locked" + self._update_icon() self._update_name() self._update_state() def _create_palette(self): - icon_name = get_icon_state(_ICON_NAME, self._model.props.strength) + icon_name = get_icon_state(_ICON_NAME, self._strength) palette_icon = Icon(icon_name=icon_name, icon_size=style.STANDARD_ICON_SIZE, badge_name=self.props.badge_name) - ap_color = self._model.get_nm_network().get_colors() - palette_icon.props.xo_color = XoColor('%s,%s' % ap_color) + palette_icon.props.xo_color = XoColor('%s,%s' % self._compute_color()) - p = palette.Palette(primary_text=self._model.props.name, + p = palette.Palette(primary_text=self._name, icon=palette_icon) self._connect_item = MenuItem(_('Connect'), 'dialog-ok') self._connect_item.connect('activate', self._activate_cb) p.menu.append(self._connect_item) - # Only show disconnect when there's a mesh device, because mesh takes - # priority over the normal wireless device. NM doesn't have a - # "disconnect" method for a device either (for various reasons) - # so this doesn't have a good mapping - if self._meshdev: - self._disconnect_item = MenuItem(_('Disconnect'), 'media-eject') - self._disconnect_item.connect('activate', - self._disconnect_activate_cb) - p.menu.append(self._disconnect_item) + self._disconnect_item = MenuItem(_('Disconnect'), 'media-eject') + self._disconnect_item.connect('activate', + self._disconnect_activate_cb) + p.menu.append(self._disconnect_item) return p @@ -117,16 +189,6 @@ class AccessPointView(CanvasPulsingIcon): self._palette.props.secondary_text = _('Disconnecting...') self.props.pulsing = False - def _strength_changed_cb(self, model, pspec): - self._update_icon() - - def _name_changed_cb(self, model, pspec): - self._update_name() - - def _state_changed_cb(self, model, pspec): - self._update_icon() - self._update_state() - def _activate_cb(self, icon): network_manager = network.get_manager() if network_manager: @@ -135,12 +197,12 @@ class AccessPointView(CanvasPulsingIcon): network_manager.set_active_device(device, nm_network) def _update_name(self): - self._palette.props.primary_text = self._model.props.name + self._palette.props.primary_text = self._name def _update_icon(self): # keep this code in sync with view/devices/network/wireless.py - strength = self._model.props.strength - if self._model.props.state == accesspoint.STATE_CONNECTED: + strength = self._strength + if self._state == _DEVICE_STATE_ACTIVATED: icon_name = '%s-connected' % _ICON_NAME else: icon_name = _ICON_NAME @@ -151,20 +213,23 @@ class AccessPointView(CanvasPulsingIcon): icon.props.icon_name = icon_name def _update_state(self): - if self._model.props.state == accesspoint.STATE_CONNECTING: + if self._state is _DEVICE_STATE_PREPARE or \ + self._state is _DEVICE_STATE_CONFIG or \ + self._state is _DEVICE_STATE_NEED_AUTH or \ + self._state is _DEVICE_STATE_IP_CONFIG: if self._disconnect_item: self._disconnect_item.show() self._connect_item.hide() self._palette.props.secondary_text = _('Connecting...') self.props.pulsing = True - elif self._model.props.state == accesspoint.STATE_CONNECTED: + elif self._state == _DEVICE_STATE_ACTIVATED: if self._disconnect_item: self._disconnect_item.show() self._connect_item.hide() # TODO: show the channel number self._palette.props.secondary_text = _('Connected') self.props.pulsing = False - elif self._model.props.state == accesspoint.STATE_NOTCONNECTED: + else: if self._disconnect_item: self._disconnect_item.hide() self._connect_item.show() @@ -176,94 +241,10 @@ class AccessPointView(CanvasPulsingIcon): self.props.pulsing = False self.props.base_color = XoColor('#D5D5D5,#D5D5D5') else: - self.props.base_color = XoColor('%s,%s' % \ - self._model.get_nm_network().get_colors()) - - def set_filter(self, query): - self._greyed_out = self._model.props.name.lower().find(query) == -1 - self._update_state() - -_MESH_ICON_NAME = 'network-mesh' - -class MeshDeviceView(CanvasPulsingIcon): - def __init__(self, nm_device, channel): - if not channel in [1, 6, 11]: - raise ValueError("Invalid channel %d" % channel) - - CanvasPulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE, - icon_name=_MESH_ICON_NAME, cache=True) - - self._nm_device = nm_device - self.channel = channel - self.props.badge_name = "badge-channel-%d" % self.channel - self._greyed_out = False - - self._disconnect_item = None - self._palette = self._create_palette() - self.set_palette(self._palette) - - pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(), - style.COLOR_TRANSPARENT.get_svg())) - self.props.pulse_color = pulse_color - - self.connect('activated', self._activate_cb) - - self._nm_device.connect('state-changed', self._state_changed_cb) - self._nm_device.connect('activation-stage-changed', - self._state_changed_cb) - self._update_state() - - def _create_palette(self): - p = palette.Palette(_("Mesh Network") + " " + str(self.channel), - menu_after_content=True) - - self._disconnect_item = gtk.MenuItem(_('Disconnect...')) - self._disconnect_item.connect('activate', self._disconnect_activate_cb) - p.menu.append(self._disconnect_item) - - state = self._nm_device.get_state() - chan = network.freq_to_channel(self._nm_device.get_frequency()) - if state == network.DEVICE_STATE_ACTIVATED and chan == self.channel: - self._disconnect_item.show() - return p - - def _disconnect_activate_cb(self, menuitem): - network_manager = network.get_manager() - if network_manager: - network_manager.set_active_device(self._nm_device) - - def _activate_cb(self, icon): - network_manager = network.get_manager() - if network_manager: - freq = network.channel_to_freq(self.channel) - network_manager.set_active_device(self._nm_device, mesh_freq=freq) - - def _state_changed_cb(self, model): - self._update_state() - - def _update_state(self): - state = self._nm_device.get_state() - chan = network.freq_to_channel(self._nm_device.get_frequency()) - if state == network.DEVICE_STATE_ACTIVATING and chan == self.channel: - self._disconnect_item.hide() - self.props.pulsing = True - elif state == network.DEVICE_STATE_ACTIVATED and chan == self.channel: - self._disconnect_item.show() - self.props.pulsing = False - elif state == network.DEVICE_STATE_INACTIVE or chan != self.channel: - self._disconnect_item.hide() - self.props.pulsing = False - - if self._greyed_out: - self.props.pulsing = False - self.props.base_color = XoColor('#D5D5D5,#D5D5D5') - else: - client = gconf.client_get_default() - color = XoColor(client.get_string('/desktop/sugar/user/color')) - self.props.base_color = color + self.props.base_color = XoColor('%s,%s' % self._compute_color()) def set_filter(self, query): - self._greyed_out = (query != '') + self._greyed_out = self._name.lower().find(query) == -1 self._update_state() class ActivityView(hippo.CanvasBox): @@ -441,8 +422,98 @@ class MeshToolbar(gtk.Toolbar): self.search_entry.activate() return False +class DeviceObserver(object): + def __init__(self, box, device): + self._box = box + self._bus = dbus.SystemBus() + self._device = device + + self._device.GetAccessPoints( + reply_handler=self._get_access_points_reply_cb, + error_handler=self._get_access_points_error_cb) + + self._bus.add_signal_receiver(self.__access_point_added_cb, + signal_name='AccessPointAdded', + path=device.object_path, + dbus_interface=_NM_WIRELESS_IFACE) + self._bus.add_signal_receiver(self.__access_point_removed_cb, + signal_name='AccessPointRemoved', + path=device.object_path, + dbus_interface=_NM_WIRELESS_IFACE) + + def _get_access_points_reply_cb(self, access_points_o): + for ap_o in access_points_o: + ap = self._bus.get_object(_NM_SERVICE, ap_o) + self._box.add_access_point(self._device, ap) + + def _get_access_points_error_cb(self, err): + logging.error('Failed to get access points: %s', err) + + def __access_point_added_cb(self, access_point_o): + ap = self._bus.get_object(_NM_SERVICE, access_point_o) + self._box.add_access_point(device, ap) + + def __access_point_removed_cb(self, access_point_o): + self._box.remove_access_point(access_point_o) + + def disconnect(self): + self._bus.add_signal_receiver(self.__device_added_cb, + signal_name='AccessPointAdded', + path=device.object_path, + dbus_interface=_NM_WIRELESS_IFACE) + self._bus.add_signal_receiver(self.__device_removed_cb, + signal_name='AccessPointRemoved', + path=device.object_path, + dbus_interface=_NM_WIRELESS_IFACE) + +class NetworkManagerObserver(object): + def __init__(self, box): + self._box = box + self._bus = dbus.SystemBus() + self._devices = {} + + obj = self._bus.get_object(_NM_SERVICE, _NM_PATH) + netmgr = dbus.Interface(obj, _NM_IFACE) + netmgr.GetDevices(reply_handler=self._get_devices_reply_cb, + error_handler=self._get_devices_error_cb) + + self._bus.add_signal_receiver(self.__device_added_cb, + signal_name='DeviceAdded', + dbus_interface=_NM_DEVICE_IFACE) + self._bus.add_signal_receiver(self.__device_removed_cb, + signal_name='DeviceRemoved', + dbus_interface=_NM_DEVICE_IFACE) + + def _get_devices_reply_cb(self, devices_o): + for dev_o in devices_o: + self._check_device(dev_o) + + def _get_devices_error_cb(self, err): + logging.error('Failed to get devices: %s', err) + + def _check_device(self, device_o): + device = self._bus.get_object(_NM_SERVICE, device_o) + props = dbus.Interface(device, 'org.freedesktop.DBus.Properties') + + device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType') + if device_type == _DEVICE_TYPE_802_11_WIRELESS: + self._devices[device_o] = DeviceObserver(self._box, device) + + def _get_device_path_error_cb(self, err): + logging.error('Failed to get device type: %s', err) + + def __device_added_cb(self, device_o): + self._check_device(device_o) + + def __device_removed_cb(self, device_o): + if device_o in self._devices: + observer = self._devices[device_o] + obsever.disconnect() + del self._devices[device_o] + class MeshBox(gtk.VBox): __gtype_name__ = 'SugarMeshBox' + def __init__(self): logging.debug("STARTUP: Loading the mesh view") @@ -487,29 +558,7 @@ class MeshBox(gtk.VBox): self._model.connect('activity-added', self._activity_added_cb) self._model.connect('activity-removed', self._activity_removed_cb) - for ap_model in self._model.get_access_points(): - self._add_access_point(ap_model) - - self._model.connect('access-point-added', - self._access_point_added_cb) - self._model.connect('access-point-removed', - self._access_point_removed_cb) - - if self._model.get_mesh(): - self.__mesh_added_cb(self._model, self._model.get_mesh()) - - self._model.connect('mesh-added', self.__mesh_added_cb) - self._model.connect('mesh-removed', self.__mesh_removed_cb) - - def __mesh_added_cb(self, model, meshdev): - self._add_mesh_icon(meshdev, 1) - self._add_mesh_icon(meshdev, 6) - self._add_mesh_icon(meshdev, 11) - - def __mesh_removed_cb(self, model): - self._remove_mesh_icon(1) - self._remove_mesh_icon(6) - self._remove_mesh_icon(11) + netmgr_observer = NetworkManagerObserver(self) def do_size_allocate(self, allocation): width = allocation.width @@ -547,20 +596,6 @@ class MeshBox(gtk.VBox): def _access_point_removed_cb(self, model, ap_model): self._remove_access_point(ap_model) - def _add_mesh_icon(self, meshdev, channel): - if self._mesh.has_key(channel): - self._remove_mesh_icon(channel) - if not meshdev: - return - self._mesh[channel] = MeshDeviceView(meshdev, channel) - self._layout.add(self._mesh[channel]) - - def _remove_mesh_icon(self, channel): - if not self._mesh.has_key(channel): - return - self._layout.remove(self._mesh[channel]) - del self._mesh[channel] - def _add_alone_buddy(self, buddy_model): icon = BuddyIcon(buddy_model) if buddy_model.is_owner(): @@ -616,20 +651,19 @@ class MeshBox(gtk.VBox): del self._activities[activity_model.get_id()] icon.destroy() - def _add_access_point(self, ap_model): - meshdev = self._model.get_mesh() - icon = AccessPointView(ap_model, meshdev) + def add_access_point(self, device, ap): + icon = AccessPointView(device, ap) self._layout.add(icon) if hasattr(icon, 'set_filter'): icon.set_filter(self._query) - self._access_points[ap_model.get_id()] = icon + self._access_points[ap.object_path] = icon - def _remove_access_point(self, ap_model): - icon = self._access_points[ap_model.get_id()] + def remove_access_point(self, ap_o): + icon = self._access_points[ap_o] self._layout.remove(icon) - del self._access_points[ap_model.get_id()] + del self._access_points[ap_o] def suspend(self): if not self._suspended: diff --git a/src/jarabe/model/neighborhood.py b/src/jarabe/model/neighborhood.py index 6111e7c..b599c40 100644 --- a/src/jarabe/model/neighborhood.py +++ b/src/jarabe/model/neighborhood.py @@ -21,7 +21,6 @@ from sugar.presence import presenceservice from sugar import activity from jarabe.model.buddy import BuddyModel -from jarabe.model.accesspoint import AccessPointModel from jarabe.model import bundleregistry from jarabe.model import network @@ -55,15 +54,7 @@ class Neighborhood(gobject.GObject): ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), 'buddy-removed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'access-point-added': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'access-point-removed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'mesh-added': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'mesh-removed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])) + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])) } def __init__(self): @@ -71,8 +62,6 @@ class Neighborhood(gobject.GObject): self._activities = {} self._buddies = {} - self._access_points = {} - self._mesh = None self._pservice = presenceservice.get_instance() self._pservice.connect("activity-appeared", @@ -90,15 +79,6 @@ class Neighborhood(gobject.GObject): self._pservice.get_activities_async( reply_handler=self._get_activities_cb) - network_manager = network.get_manager() - if network_manager: - for nm_device in network_manager.get_devices(): - self._add_network_device(nm_device) - network_manager.connect('device-added', - self._nm_device_added_cb) - network_manager.connect('device-removed', - self._nm_device_removed_cb) - def _get_buddies_cb(self, buddy_list): for buddy in buddy_list: self._buddy_appeared_cb(self._pservice, buddy) @@ -107,61 +87,6 @@ class Neighborhood(gobject.GObject): for act in activity_list: self._check_activity(act) - def _nm_device_added_cb(self, manager, nm_device): - self._add_network_device(nm_device) - - def _nm_device_removed_cb(self, manager, nm_device): - self._remove_network_device(nm_device) - - def _nm_network_appeared_cb(self, nm_device, nm_network): - self._add_access_point(nm_device, nm_network) - - def _nm_network_disappeared_cb(self, nm_device, nm_network): - if self._access_points.has_key(nm_network.get_op()): - ap = self._access_points[nm_network.get_op()] - self._remove_access_point(ap) - - def _add_network_device(self, nm_device): - dtype = nm_device.get_type() - if dtype == network.DEVICE_TYPE_802_11_WIRELESS: - for nm_network in nm_device.get_networks(): - self._add_access_point(nm_device, nm_network) - - nm_device.connect('network-appeared', - self._nm_network_appeared_cb) - nm_device.connect('network-disappeared', - self._nm_network_disappeared_cb) - elif dtype == network.DEVICE_TYPE_802_11_MESH_OLPC: - self._mesh = nm_device - self.emit('mesh-added', self._mesh) - - def _remove_network_device(self, nm_device): - if nm_device == self._mesh: - self._mesh = None - self.emit('mesh-removed') - elif nm_device.get_type() == network.DEVICE_TYPE_802_11_WIRELESS: - aplist = self._access_points.values() - for ap in aplist: - if ap.get_nm_device() == nm_device: - self._remove_access_point(ap) - - def _add_access_point(self, nm_device, nm_network): - model = AccessPointModel(nm_device, nm_network) - self._access_points[model.get_id()] = model - self.emit('access-point-added', model) - - def _remove_access_point(self, ap): - if not self._access_points.has_key(ap.get_id()): - return - self.emit('access-point-removed', ap) - del self._access_points[ap.get_id()] - - def get_mesh(self): - return self._mesh - - def get_access_points(self): - return self._access_points.values() - def get_activities(self): return self._activities.values() |