Ticket #230: 0001-Implement-mesh-support-again-dsd.patch

File 0001-Implement-mesh-support-again-dsd.patch, 35.4 KB (added by tomeu, 14 years ago)

complete patch

  • extensions/deviceicon/network.py

    From c4e6fb13fa5255011911d931d9125e2e7467893e Mon Sep 17 00:00:00 2001
    From: Tomeu Vizoso <tomeu@sugarlabs.org>
    Date: Sat, 16 Jan 2010 19:12:30 +0100
    Subject: [PATCH] Implement mesh support again (dsd)
    
    ---
     extensions/deviceicon/network.py |  170 ++++++++++++++++++++++++++---
     src/jarabe/desktop/meshbox.py    |  222 ++++++++++++++++++++++++++++++++++++--
     src/jarabe/model/Makefile.am     |    1 +
     src/jarabe/model/network.py      |   32 +++++-
     src/jarabe/model/olpcmesh.py     |  214 ++++++++++++++++++++++++++++++++++++
     5 files changed, 612 insertions(+), 27 deletions(-)
     create mode 100644 src/jarabe/model/olpcmesh.py
    
    diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
    index 3f27ec2..e40e196 100644
    a b from sugar.graphics.toolbutton import ToolButton 
    3535from sugar.graphics.tray import TrayIcon
    3636from sugar.graphics import xocolor
    3737from sugar.util import unique_id
     38from sugar import profile
    3839
    3940from jarabe.model import network
    4041from jarabe.model.network import Settings
    _NM_PATH = '/org/freedesktop/NetworkManager' 
    5051_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
    5152_NM_WIRED_IFACE = 'org.freedesktop.NetworkManager.Device.Wired'
    5253_NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless'
     54_NM_OLPC_MESH_IFACE = 'org.freedesktop.NetworkManager.Device.OlpcMesh'
    5355_NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
    5456_NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
    5557
    class WirelessPalette(Palette): 
    8183                                   gobject.TYPE_NONE, ([])),
    8284    }
    8385
    84     def __init__(self, primary_text):
     86    def __init__(self, primary_text, can_create=True):
    8587        Palette.__init__(self, label=primary_text)
    8688
    8789        self._disconnect_item = None
    class WirelessPalette(Palette): 
    112114        self._disconnect_item.connect('activate', self.__disconnect_activate_cb)
    113115        self.menu.append(self._disconnect_item)
    114116
    115         self._adhoc_item = gtk.MenuItem(_('Create new wireless network'))
    116         self._adhoc_item.connect('activate', self.__adhoc_activate_cb)
    117         self.menu.append(self._adhoc_item)
    118         self._adhoc_item.show()
     117        if can_create:
     118            self._adhoc_item = gtk.MenuItem(_('Create new wireless network'))
     119            self._adhoc_item.connect('activate', self.__adhoc_activate_cb)
     120            self.menu.append(self._adhoc_item)
     121            self._adhoc_item.show()
    119122
    120123    def set_connecting(self):
    121124        self.props.secondary_text = _('Connecting...')
    122125
    123     def set_connected(self, frequency, iaddress):
     126    def _set_connected(self, iaddress):
    124127        self.set_content(self._info)
    125128        self.props.secondary_text = _('Connected')
    126         self._set_channel(frequency)
    127129        self._set_ip_address(iaddress)
    128130        self._disconnect_item.show()
    129        
     131
     132    def set_connected_with_frequency(self, frequency, iaddress):
     133        self._set_connected(iaddress)
     134        self._set_frequency(frequency)
     135
     136    def set_connected_with_channel(self, channel, iaddress):
     137        self._set_connected(iaddress)
     138        self._set_channel(channel)
     139
    130140    def set_disconnected(self):
    131141        self.props.primary_text = ''
    132142        self.props.secondary_text = ''
    class WirelessPalette(Palette): 
    139149    def __adhoc_activate_cb(self, menuitem):
    140150        self.emit('create-connection')
    141151
    142     def _set_channel(self, frequency):
     152    def _set_frequency(self, frequency):
    143153        try:
    144154            channel = frequency_to_channel(frequency)
    145155        except KeyError:
    146156            channel = 0
     157        self._set_channel(channel)
     158
     159    def _set_channel(self, channel):
    147160        self._channel_label.set_text("%s: %d" % (_("Channel"), channel))
    148161
    149162    def _set_ip_address(self, ip_address):
    class WirelessDeviceView(ToolButton): 
    386399            self._icon.props.pulsing = True
    387400        elif state == network.DEVICE_STATE_ACTIVATED:
    388401            address = self._device_props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
    389             self._palette.set_connected(self._frequency, address)
     402            self._palette.set_connected_with_frequency(self._frequency, address)
    390403            self._icon.props.pulsing = False
    391404        else:
    392405            self._icon.props.badge_name = None
    class WirelessDeviceView(ToolButton): 
    468481    def __activate_error_cb(self, err):
    469482        logging.debug('Failed to create network: %s', err)
    470483
     484
     485class OlpcMeshDeviceView(ToolButton):
     486    _ICON_NAME = 'network-mesh'
     487    FRAME_POSITION_RELATIVE = 302
     488
     489    def __init__(self, device):
     490        ToolButton.__init__(self)
     491
     492        self._bus = dbus.SystemBus()
     493        self._device = device
     494        self._device_props = None
     495        self._device_state = None
     496        self._channel = 0
     497
     498        self._icon = PulsingIcon(icon_name=self._ICON_NAME)
     499        self._icon.props.pulse_color = xocolor.XoColor( \
     500            "%s,%s" % (style.COLOR_BUTTON_GREY.get_svg(),
     501                       style.COLOR_TRANSPARENT.get_svg()))
     502        self._icon.props.base_color = profile.get_color()
     503
     504        self.set_icon_widget(self._icon)
     505        self._icon.show()
     506
     507        self.set_palette_invoker(FrameWidgetInvoker(self))
     508        self._palette = WirelessPalette(_("Mesh Network"))
     509        self._palette.connect('deactivate-connection',
     510                              self.__deactivate_connection)
     511        self.set_palette(self._palette)
     512        self._palette.set_group_id('frame')
     513
     514        self._device_props = dbus.Interface(self._device,
     515                                            'org.freedesktop.DBus.Properties')
     516        self._device_props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
     517                              reply_handler=self.__get_device_props_reply_cb,
     518                              error_handler=self.__get_device_props_error_cb)
     519        self._device_props.Get(_NM_OLPC_MESH_IFACE, 'ActiveChannel',
     520                            reply_handler=self.__get_active_channel_reply_cb,
     521                            error_handler=self.__get_active_channel_error_cb)
     522
     523        self._bus.add_signal_receiver(self.__state_changed_cb,
     524                                      signal_name='StateChanged',
     525                                      path=self._device.object_path,
     526                                      dbus_interface=_NM_DEVICE_IFACE)
     527        self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
     528                                      signal_name='PropertiesChanged',
     529                                      path=device.object_path,
     530                                      dbus_interface=_NM_OLPC_MESH_IFACE)
     531
     532    def disconnect(self):
     533        self._bus.remove_signal_receiver(self.__state_changed_cb,
     534                                         signal_name='StateChanged',
     535                                         path=self._device.object_path,
     536                                         dbus_interface=_NM_DEVICE_IFACE)
     537        self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
     538                                         signal_name='PropertiesChanged',
     539                                         path=self._device.object_path,
     540                                         dbus_interface=_NM_OLPC_MESH_IFACE)
     541
     542    def __get_device_props_reply_cb(self, properties):
     543        if 'State' in properties:
     544            self._device_state = properties['State']
     545            self._update()
     546
     547    def __get_device_props_error_cb(self, err):
     548        logging.error('Error getting the device properties: %s', err)
     549
     550    def __get_active_channel_reply_cb(self, channel):
     551        self._channel = channel
     552        self._update_text()
     553
     554    def __get_active_channel_error_cb(self, err):
     555        logging.error('Error getting the active channel: %s', err)
     556
     557    def __state_changed_cb(self, new_state, old_state, reason):
     558        self._device_state = new_state
     559        self._update()
     560
     561    def __wireless_properties_changed_cb(self, properties):
     562        if 'ActiveChannel' in properties:
     563            self._channel = properties['ActiveChannel']
     564            self._update_text()
     565
     566    def _update_text(self):
     567        text = _("Mesh Network") + " " + str(self._channel)
     568        self._palette.props.primary_text = text
     569
     570    def _update(self):
     571        state = self._device_state
     572
     573        if state in [network.DEVICE_STATE_PREPARE,
     574                     network.DEVICE_STATE_CONFIG,
     575                     network.DEVICE_STATE_NEED_AUTH,
     576                     network.DEVICE_STATE_IP_CONFIG]:
     577            self._palette.set_connecting()
     578            self._icon.props.pulsing = True
     579        elif state == network.DEVICE_STATE_ACTIVATED:
     580            address = self._device_props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
     581            self._palette.set_connected_with_channel(self._channel, address)
     582            self._icon.props.pulsing = False
     583
     584    def __deactivate_connection(self, palette, data=None):
     585        obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
     586        netmgr = dbus.Interface(obj, _NM_IFACE)
     587        netmgr_props = dbus.Interface(netmgr, 'org.freedesktop.DBus.Properties')
     588        active_connections_o = netmgr_props.Get(_NM_IFACE,
     589                                                'ActiveConnections')
     590
     591        for conn_o in active_connections_o:
     592            # The connection path for a mesh connection is the device itself.
     593            obj = self._bus.get_object(_NM_IFACE, conn_o)
     594            props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
     595            ap_op = props.Get(_NM_ACTIVE_CONN_IFACE, 'SpecificObject')
     596
     597            try:
     598                obj = self._bus.get_object(_NM_IFACE, ap_op)
     599                props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
     600                type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
     601                if type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     602                    netmgr.DeactivateConnection(conn_o)
     603                    break
     604            except dbus.exceptions.DBusException:
     605                pass
     606
     607
    471608class WiredDeviceView(TrayIcon):
    472609
    473610    _ICON_NAME = 'network-wired'
    class WiredDeviceView(TrayIcon): 
    487624
    488625
    489626class WirelessDeviceObserver(object):
    490     def __init__(self, device, tray):
     627    def __init__(self, device, tray, device_type):
    491628        self._device = device
    492629        self._device_view = None
    493630        self._tray = tray
    494631
    495         self._device_view = WirelessDeviceView(self._device)
     632        if device_type == network.DEVICE_TYPE_802_11_WIRELESS:
     633            self._device_view = WirelessDeviceView(self._device)
     634        elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     635            self._device_view = OlpcMeshDeviceView(self._device)
     636
    496637        self._tray.add_device(self._device_view)
    497638
    498639    def disconnect(self):
    class NetworkManagerObserver(object): 
    590731        if device_type == network.DEVICE_TYPE_802_3_ETHERNET:
    591732            device = WiredDeviceObserver(nm_device, self._tray)
    592733            self._devices[device_op] = device
    593         elif device_type == network.DEVICE_TYPE_802_11_WIRELESS:
    594             device = WirelessDeviceObserver(nm_device, self._tray)
     734        elif device_type in [network.DEVICE_TYPE_802_11_WIRELESS,
     735                             network.DEVICE_TYPE_802_11_OLPC_MESH]:
     736            device = WirelessDeviceObserver(nm_device, self._tray, device_type)
    595737            self._devices[device_op] = device
    596738
    597739    def __device_added_cb(self, device_op):
  • src/jarabe/desktop/meshbox.py

    diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
    index 7b8e3a7..38f1e71 100644
    a b from sugar.graphics.menuitem import MenuItem 
    3636from sugar.activity.activityhandle import ActivityHandle
    3737from sugar.activity import activityfactory
    3838from sugar.util import unique_id
     39from sugar import profile
    3940
    4041from jarabe.model import neighborhood
    4142from jarabe.view.buddyicon import BuddyIcon
    from jarabe.model.network import Settings 
    5152from jarabe.model.network import IP4Config
    5253from jarabe.model.network import WirelessSecurity
    5354from jarabe.model.network import AccessPoint
     55from jarabe.model.network import OlpcMesh as OlpcMeshSettings
     56from jarabe.model.olpcmesh import OlpcMeshManager
    5457
    5558_NM_SERVICE = 'org.freedesktop.NetworkManager'
    5659_NM_IFACE = 'org.freedesktop.NetworkManager'
    5760_NM_PATH = '/org/freedesktop/NetworkManager'
    5861_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
    5962_NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless'
     63_NM_OLPC_MESH_IFACE = 'org.freedesktop.NetworkManager.Device.OlpcMesh'
    6064_NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
    6165_NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
    6266
    63 _ICON_NAME = 'network-wireless'
    64 
     67_AP_ICON_NAME = 'network-wireless'
     68_OLPC_MESH_ICON_NAME = 'network-mesh'
    6569
    6670class WirelessNetworkView(CanvasPulsingIcon):
    6771    def __init__(self, initial_ap):
    class WirelessNetworkView(CanvasPulsingIcon): 
    149153            and self._name[-15] == '#'
    150154
    151155    def _create_palette(self):
    152         icon_name = get_icon_state(_ICON_NAME, self._strength)
     156        icon_name = get_icon_state(_AP_ICON_NAME, self._strength)
    153157        self._palette_icon = Icon(icon_name=icon_name,
    154158                                  icon_size=style.STANDARD_ICON_SIZE,
    155159                                  badge_name=self.props.badge_name)
    class WirelessNetworkView(CanvasPulsingIcon): 
    224228                if self._mode == network.NM_802_11_MODE_INFRA:
    225229                    connection.set_connected()
    226230
    227             icon_name = '%s-connected' % _ICON_NAME
     231            icon_name = '%s-connected' % _AP_ICON_NAME
    228232        else:
    229             icon_name = _ICON_NAME
     233            icon_name = _AP_ICON_NAME
    230234
    231235        icon_name = get_icon_state(icon_name, self._strength)
    232236        if icon_name:
    class WirelessNetworkView(CanvasPulsingIcon): 
    419423            return None
    420424        return self._access_points[ap_path]
    421425
     426    def is_olpc_mesh(self):
     427        return self._mode == network.NM_802_11_MODE_ADHOC \
     428            and self.name == "olpc-mesh"
     429
     430    def remove_all_aps(self):
     431        for ap in self._access_points.values():
     432            ap.disconnect()
     433        self._access_points = {}
     434        self._active_ap = None
     435        self.update_strength()
     436
    422437    def disconnect(self):
    423438        self._bus.remove_signal_receiver(self.__device_state_changed_cb,
    424439                                         signal_name='StateChanged',
    class WirelessNetworkView(CanvasPulsingIcon): 
    430445                                         dbus_interface=_NM_WIRELESS_IFACE)
    431446
    432447
     448class OlpcMeshView(CanvasPulsingIcon):
     449    def __init__(self, mesh_mgr, channel):
     450        CanvasPulsingIcon.__init__(self, icon_name=_OLPC_MESH_ICON_NAME,
     451                                   size=style.STANDARD_ICON_SIZE, cache=True)
     452        self._bus = dbus.SystemBus()
     453        self._channel = channel
     454        self._mesh_mgr = mesh_mgr
     455        self._disconnect_item = None
     456        self._connect_item = None
     457        self._greyed_out = False
     458        self._name = ''
     459        self._device_state = None
     460        self._connection = None
     461        self._active = False
     462        device = mesh_mgr.mesh_device
     463
     464        self.connect('button-release-event', self.__button_release_event_cb)
     465
     466        interface_props = dbus.Interface(device,
     467                                         'org.freedesktop.DBus.Properties')
     468        interface_props.Get(_NM_DEVICE_IFACE, 'State',
     469                            reply_handler=self.__get_device_state_reply_cb,
     470                            error_handler=self.__get_device_state_error_cb)
     471        interface_props.Get(_NM_OLPC_MESH_IFACE, 'ActiveChannel',
     472                            reply_handler=self.__get_active_channel_reply_cb,
     473                            error_handler=self.__get_active_channel_error_cb)
     474
     475        self._bus.add_signal_receiver(self.__device_state_changed_cb,
     476                                      signal_name='StateChanged',
     477                                      path=device.object_path,
     478                                      dbus_interface=_NM_DEVICE_IFACE)
     479        self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
     480                                      signal_name='PropertiesChanged',
     481                                      path=device.object_path,
     482                                      dbus_interface=_NM_OLPC_MESH_IFACE)
     483
     484        pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
     485                                         style.COLOR_TRANSPARENT.get_svg()))
     486        self.props.pulse_color = pulse_color
     487        self.props.base_color = profile.get_color()
     488        self._palette = self._create_palette()
     489        self.set_palette(self._palette)
     490
     491    def _create_palette(self):
     492        _palette = palette.Palette(_("Mesh Network %d") % self._channel)
     493
     494        self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
     495        self._connect_item.connect('activate', self.__connect_activate_cb)
     496        _palette.menu.append(self._connect_item)
     497
     498        return _palette
     499
     500    def __get_device_state_reply_cb(self, state):
     501        self._device_state = state
     502        self._update()
     503
     504    def __get_device_state_error_cb(self, err):
     505        logging.error('Error getting the device state: %s', err)
     506
     507    def __device_state_changed_cb(self, new_state, old_state, reason):
     508        self._device_state = new_state
     509        self._update()
     510
     511    def __get_active_channel_reply_cb(self, channel):
     512        self._active = (channel == self._channel)
     513        self._update()
     514
     515    def __get_active_channel_error_cb(self, err):
     516        logging.error('Error getting the active channel: %s', err)
     517
     518    def __wireless_properties_changed_cb(self, properties):
     519        if 'ActiveChannel' in properties:
     520            channel = properties['ActiveChannel']
     521            self._active = (channel == self._channel)
     522            self._update()
     523
     524    def _update(self):
     525        if self._active:
     526            state = self._device_state
     527        else:
     528            state = network.DEVICE_STATE_UNKNOWN
     529
     530        if state in [network.DEVICE_STATE_PREPARE,
     531                     network.DEVICE_STATE_CONFIG,
     532                     network.DEVICE_STATE_NEED_AUTH,
     533                     network.DEVICE_STATE_IP_CONFIG]:
     534            if self._disconnect_item:
     535                self._disconnect_item.show()
     536            self._connect_item.hide()
     537            self._palette.props.secondary_text = _('Connecting...')
     538            self.props.pulsing = True
     539        elif state == network.DEVICE_STATE_ACTIVATED:
     540            if self._disconnect_item:
     541                self._disconnect_item.show()
     542            self._connect_item.hide()
     543            self._palette.props.secondary_text = _('Connected')
     544            self.props.pulsing = False
     545        else:
     546            if self._disconnect_item:
     547                self._disconnect_item.hide()
     548            self._connect_item.show()
     549            self._palette.props.secondary_text = None
     550            self.props.pulsing = False
     551
     552    def _update_color(self):
     553        if self._greyed_out:
     554            self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
     555        else:
     556            self.props.base_color = profile.get_color()
     557
     558    def __connect_activate_cb(self, icon):
     559        self._connect()
     560
     561    def __button_release_event_cb(self, icon, event):
     562        self._connect()
     563
     564    def _connect(self):
     565        self._mesh_mgr.user_activate_channel(self._channel)
     566
     567    def __activate_reply_cb(self, connection):
     568        logging.debug('Connection activated: %s', connection)
     569
     570    def __activate_error_cb(self, err):
     571        logging.error('Failed to activate connection: %s', err)
     572
     573    def set_filter(self, query):
     574        self._greyed_out = (query != '')
     575        self._update_color()
     576
     577    def disconnect(self):
     578        self._bus.remove_signal_receiver(self.__device_state_changed_cb,
     579                                         signal_name='StateChanged',
     580                                         path=self._device.object_path,
     581                                         dbus_interface=_NM_DEVICE_IFACE)
     582        self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
     583                                         signal_name='PropertiesChanged',
     584                                         path=self._device.object_path,
     585                                         dbus_interface=_NM_OLPC_MESH_IFACE)
     586
     587
    433588class ActivityView(hippo.CanvasBox):
    434589    def __init__(self, model):
    435590        hippo.CanvasBox.__init__(self)
    class NetworkManagerObserver(object): 
    730885        device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
    731886        if device_type == network.DEVICE_TYPE_802_11_WIRELESS:
    732887            self._devices[device_o] = DeviceObserver(self._box, device)
     888        elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     889            self._box.enable_olpc_mesh(device)
    733890
    734891    def _get_device_path_error_cb(self, err):
    735892        logging.error('Failed to get device type: %s', err)
    class NetworkManagerObserver(object): 
    742899            observer = self._devices[device_o]
    743900            observer.disconnect()
    744901            del self._devices[device_o]
    745 
     902            return
     903           
     904        device = self._bus.get_object(_NM_SERVICE, device_o)
     905        props = dbus.Interface(device, 'org.freedesktop.DBus.Properties')
     906        device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
     907        if device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     908            self._box.disable_olpc_mesh(device)
    746909
    747910class MeshBox(gtk.VBox):
    748911    __gtype_name__ = 'SugarMeshBox'
    class MeshBox(gtk.VBox): 
    757920        self._model = neighborhood.get_model()
    758921        self._buddies = {}
    759922        self._activities = {}
    760         self._mesh = {}
     923        self._mesh = []
    761924        self._buddy_to_activity = {}
    762925        self._suspended = True
    763926        self._query = ''
    class MeshBox(gtk.VBox): 
    9021065            del self.wireless_networks[hash]
    9031066
    9041067    def _ap_props_changed_cb(self, ap, old_hash):
     1068        # if we have mesh hardware, ignore OLPC mesh networks that appear as
     1069        # normal wifi networks
     1070        if len(self._mesh) > 0 and ap.mode == network.NM_802_11_MODE_ADHOC \
     1071                and ap.name == "olpc-mesh":
     1072            logging.debug("ignoring OLPC mesh IBSS")
     1073            ap.disconnect()
     1074            return
     1075
    9051076        if old_hash is None: # new AP finished initializing
    9061077            self._add_ap_to_network(ap)
    9071078            return
    class MeshBox(gtk.VBox): 
    9361107            self._remove_net_if_empty(net, ap.network_hash())
    9371108            return
    9381109
    939         logging.error('Can not remove access point %s', ap_o)
     1110        # it's not an error if the AP isn't found, since we might have ignored
     1111        # it (e.g. olpc-mesh adhoc network)
     1112        logging.debug('Can not remove access point %s' % ap_o)
     1113
     1114    def _add_olpc_mesh_icon(self, mesh_mgr, channel):
     1115        icon = OlpcMeshView(mesh_mgr, channel)
     1116        self._layout.add(icon)
     1117        self._mesh.append(icon)
     1118
     1119    def enable_olpc_mesh(self, mesh_device):
     1120        mesh_mgr = OlpcMeshManager(mesh_device)
     1121        self._add_olpc_mesh_icon(mesh_mgr, 1)
     1122        self._add_olpc_mesh_icon(mesh_mgr, 6)
     1123        self._add_olpc_mesh_icon(mesh_mgr, 11)
     1124
     1125        # the OLPC mesh can be recognised as a "normal" wifi network. remove
     1126        # any such normal networks if they have been created
     1127        for hash, net in self.wireless_networks.iteritems():
     1128            if not net.is_olpc_mesh():
     1129                continue
     1130
     1131            logging.debug("removing OLPC mesh IBSS")
     1132            net.remove_all_aps()
     1133            net.disconnect()
     1134            self._layout.remove(net)
     1135            del self.wireless_networks[hash]
     1136
     1137    def disable_olpc_mesh(self, mesh_device):
     1138        for icon in self._mesh:
     1139            icon.disconnect()
     1140            self._layout.remove(icon)
     1141        self._mesh = []
    9401142
    9411143    def suspend(self):
    9421144        if not self._suspended:
    9431145            self._suspended = True
    944             for net in self.wireless_networks.values():
     1146            for net in self.wireless_networks.values() + self._mesh:
    9451147                net.props.paused = True
    9461148
    9471149    def resume(self):
    9481150        if self._suspended:
    9491151            self._suspended = False
    950             for net in self.wireless_networks.values():
     1152            for net in self.wireless_networks.values() + self._mesh:
    9511153                net.props.paused = False
    9521154
    9531155    def _toolbar_query_changed_cb(self, toolbar, query):
  • src/jarabe/model/Makefile.am

    diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am
    index 399db65..18d44da 100644
    a b sugar_PYTHON = \ 
    66        filetransfer.py         \
    77        friends.py              \
    88        invites.py              \
     9        olpcmesh.py             \
    910        owner.py                \
    1011        neighborhood.py         \
    1112        network.py              \
  • src/jarabe/model/network.py

    diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py
    index 10b73ab..b3c30d9 100644
    a b from sugar import env 
    2929
    3030DEVICE_TYPE_802_3_ETHERNET = 1
    3131DEVICE_TYPE_802_11_WIRELESS = 2
     32DEVICE_TYPE_802_11_OLPC_MESH = 6
    3233
    3334DEVICE_STATE_UNKNOWN = 0
    3435DEVICE_STATE_UNMANAGED = 1
    class WirelessSecurity(object): 
    103104        return wireless_security
    104105
    105106class Wireless(object):
     107    nm_name = "802-11-wireless"
     108
    106109    def __init__(self):
    107110        self.ssid = None
    108111        self.security = None
    class Wireless(object): 
    119122            wireless['band'] = self.band
    120123        return wireless
    121124
     125
     126class OlpcMesh(object):
     127    nm_name = "802-11-olpc-mesh"
     128
     129    def __init__(self, channel, anycast_addr):
     130        self.channel = channel
     131        self.anycast_addr = anycast_addr
     132
     133    def get_dict(self):
     134        ret = {
     135            "ssid": dbus.ByteArray("olpc-mesh"),
     136            "channel": self.channel,
     137        }
     138
     139        if self.anycast_addr:
     140            ret["dhcp-anycast-address"] = dbus.ByteArray(self.anycast_addr)
     141        return ret
     142
     143
    122144class Connection(object):
    123145    def __init__(self):
    124146        self.id = None
    class IP4Config(object): 
    147169        return ip4_config
    148170
    149171class Settings(object):
    150     def __init__(self):
     172    def __init__(self, wireless_cfg=None):
    151173        self.connection = Connection()
    152         self.wireless = Wireless()
    153174        self.ip4_config = None
    154175        self.wireless_security = None
    155176
     177        if wireless_cfg is not None:
     178            self.wireless = wireless_cfg
     179        else:
     180            self.wireless = Wireless()
     181
    156182    def get_dict(self):
    157183        settings = {}
    158184        settings['connection'] = self.connection.get_dict()
    159         settings['802-11-wireless'] = self.wireless.get_dict()
     185        settings[self.wireless.nm_name] = self.wireless.get_dict()
    160186        if self.wireless_security is not None:
    161187            settings['802-11-wireless-security'] = \
    162188                self.wireless_security.get_dict()
  • new file src/jarabe/model/olpcmesh.py

    diff --git a/src/jarabe/model/olpcmesh.py b/src/jarabe/model/olpcmesh.py
    new file mode 100644
    index 0000000..cbd7ddd
    - +  
     1# Copyright (C) 2009 One Laptop per Child
     2#
     3# This program is free software; you can redistribute it and/or modify
     4# it under the terms of the GNU General Public License as published by
     5# the Free Software Foundation; either version 2 of the License, or
     6# (at your option) any later version.
     7#
     8# This program is distributed in the hope that it will be useful,
     9# but WITHOUT ANY WARRANTY; without even the implied warranty of
     10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11# GNU General Public License for more details.
     12#
     13# You should have received a copy of the GNU General Public License
     14# along with this program; if not, write to the Free Software
     15# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     16
     17import logging
     18
     19import dbus
     20import gobject
     21
     22from jarabe.model import network
     23from jarabe.model.network import Settings
     24from jarabe.model.network import OlpcMesh as OlpcMeshSettings
     25from sugar.util import unique_id
     26
     27_NM_SERVICE = 'org.freedesktop.NetworkManager'
     28_NM_IFACE = 'org.freedesktop.NetworkManager'
     29_NM_PATH = '/org/freedesktop/NetworkManager'
     30_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
     31_NM_OLPC_MESH_IFACE = 'org.freedesktop.NetworkManager.Device.OlpcMesh'
     32
     33_XS_ANYCAST = "\xc0\x27\xc0\x27\xc0\x00"
     34
     35DEVICE_STATE_UNKNOWN = 0
     36DEVICE_STATE_UNMANAGED = 1
     37DEVICE_STATE_UNAVAILABLE = 2
     38DEVICE_STATE_DISCONNECTED = 3
     39DEVICE_STATE_PREPARE = 4
     40DEVICE_STATE_CONFIG = 5
     41DEVICE_STATE_NEED_AUTH = 6
     42DEVICE_STATE_IP_CONFIG = 7
     43DEVICE_STATE_ACTIVATED = 8
     44DEVICE_STATE_FAILED = 9
     45
     46class OlpcMeshManager(object):
     47    def __init__(self, mesh_device):
     48        self._bus = dbus.SystemBus()
     49
     50        self.mesh_device = mesh_device
     51        self.eth_device = self._get_companion_device()
     52
     53        self._connection_queue = []
     54        """Stack of connections that we'll iterate through until we find one
     55           that works.
     56
     57        """
     58
     59        props = dbus.Interface(self.mesh_device,
     60                               'org.freedesktop.DBus.Properties')
     61        props.Get(_NM_DEVICE_IFACE, 'State',
     62                  reply_handler=self.__get_mesh_state_reply_cb,
     63                  error_handler=self.__get_state_error_cb)
     64
     65        props = dbus.Interface(self.eth_device,
     66                               'org.freedesktop.DBus.Properties')
     67        props.Get(_NM_DEVICE_IFACE, 'State',
     68                  reply_handler=self.__get_eth_state_reply_cb,
     69                  error_handler=self.__get_state_error_cb)
     70
     71        self._bus.add_signal_receiver(self.__eth_device_state_changed_cb,
     72                                      signal_name='StateChanged',
     73                                      path=self.eth_device.object_path,
     74                                      dbus_interface=_NM_DEVICE_IFACE)
     75
     76        self._bus.add_signal_receiver(self.__mshdev_state_changed_cb,
     77                                      signal_name='StateChanged',
     78                                      path=self.mesh_device.object_path,
     79                                      dbus_interface=_NM_DEVICE_IFACE)
     80
     81        self._idle_source = 0
     82        self._mesh_device_state = DEVICE_STATE_UNKNOWN
     83        self._eth_device_state = DEVICE_STATE_UNKNOWN
     84
     85        if self._have_configured_connections():
     86            self._start_automesh()
     87        else:
     88            self._start_automesh_timer()
     89
     90    def _get_companion_device(self):
     91        props = dbus.Interface(self.mesh_device,
     92                               'org.freedesktop.DBus.Properties')
     93        eth_device_o = props.Get(_NM_OLPC_MESH_IFACE, 'Companion')
     94        return self._bus.get_object(_NM_SERVICE, eth_device_o)
     95
     96    def _have_configured_connections(self):
     97        return len(network.get_settings().connections) > 0
     98
     99    def _start_automesh_timer(self):
     100        """Start our timer system which basically looks for 10 seconds of
     101           inactivity on both devices, then starts automesh.
     102
     103        """
     104        if self._idle_source != 0:
     105            gobject.source_remove(self._idle_source)
     106        self._idle_source = gobject.timeout_add_seconds(10, self._idle_check)
     107
     108    def __get_state_error_cb(self, err):
     109        logging.debug('Error getting the device state: %s', err)
     110
     111    def __get_mesh_state_reply_cb(self, state):
     112        self._mesh_device_state = state
     113        self._maybe_schedule_idle_check()
     114
     115    def __get_eth_state_reply_cb(self, state):
     116        self._eth_device_state = state
     117        self._maybe_schedule_idle_check()
     118
     119    def __eth_device_state_changed_cb(self, new_state, old_state, reason):
     120        """If a connection is activated on the eth device, stop trying our
     121        automatic connections.
     122       
     123        """
     124        self._eth_device_state = new_state
     125        self._maybe_schedule_idle_check()
     126
     127        if new_state >= DEVICE_STATE_PREPARE \
     128                and new_state <= DEVICE_STATE_ACTIVATED \
     129                and len(self._connection_queue) > 0:
     130            self._connection_queue = []
     131
     132    def __mshdev_state_changed_cb(self, new_state, old_state, reason):
     133        self._mesh_device_state = new_state
     134        self._maybe_schedule_idle_check()
     135
     136        if new_state == DEVICE_STATE_FAILED:
     137            self._try_next_connection_from_queue()
     138        elif new_state == DEVICE_STATE_ACTIVATED \
     139                and len(self._connection_queue) > 0:
     140            self._empty_connection_queue()
     141
     142    def _maybe_schedule_idle_check(self):
     143        if self._mesh_device_state == DEVICE_STATE_DISCONNECTED \
     144                and self._eth_device_state == DEVICE_STATE_DISCONNECTED:
     145            self._start_automesh_timer()
     146
     147    def _idle_check(self):
     148        if self._mesh_device_state == DEVICE_STATE_DISCONNECTED \
     149                and self._eth_device_state == DEVICE_STATE_DISCONNECTED:
     150            logging.debug("starting automesh due to inactivity")
     151            self._start_automesh()
     152        return False
     153
     154    def _make_connection(self, channel, anycast_address=None):
     155        wireless_config = OlpcMeshSettings(channel, anycast_address)
     156        settings = Settings(wireless_cfg=wireless_config)
     157        if not anycast_address:
     158            settings.ip4_config = network.IP4Config()
     159            settings.ip4_config.method = 'link-local'
     160        settings.connection.id = 'olpc-mesh-' + str(channel)
     161        settings.connection.uuid = unique_id()
     162        settings.connection.type = '802-11-olpc-mesh'
     163        connection = network.add_connection(settings.connection.id, settings)
     164        return connection
     165
     166    def __activate_reply_cb(self, connection):
     167        logging.debug('Connection activated: %s', connection)
     168
     169    def __activate_error_cb(self, err):
     170        logging.error('Failed to activate connection: %s', err)
     171
     172    def _activate_connection(self, channel, anycast_address=None):
     173        logging.debug("activate channel %d anycast %s",
     174                      (channel, repr(anycast_address)))
     175        proxy = self._bus.get_object(_NM_SERVICE, _NM_PATH)
     176        network_manager = dbus.Interface(proxy, _NM_IFACE)
     177        connection = self._make_connection(channel, anycast_address)
     178
     179        network_manager.ActivateConnection(network.SETTINGS_SERVICE,
     180                connection.path,
     181                self.mesh_device.object_path,
     182                self.mesh_device.object_path,
     183                reply_handler=self.__activate_reply_cb,
     184                error_handler=self.__activate_error_cb)
     185
     186    def _try_next_connection_from_queue(self):
     187        if len(self._connection_queue) == 0:
     188            return
     189
     190        channel, anycast = self._connection_queue.pop()
     191        self._activate_connection(channel, anycast)
     192
     193    def _empty_connection_queue(self):
     194        self._connection_queue = []
     195
     196    def user_activate_channel(self, channel):
     197        """Activate a mesh connection on a user-specified channel.
     198        Looks for XS first, then resorts to simple mesh."""
     199        self._empty_connection_queue()
     200        self._connection_queue.append((channel, None))
     201        self._connection_queue.append((channel, _XS_ANYCAST))
     202        self._try_next_connection_from_queue()
     203
     204    def _start_automesh(self):
     205        """Start meshing automatically, intended when there are no better
     206        networks to connect to. First looks for XS on all channels, then falls
     207        back to simple mesh on channel 1."""
     208        self._empty_connection_queue()
     209        self._connection_queue.append((1, None))
     210        self._connection_queue.append((11, _XS_ANYCAST))
     211        self._connection_queue.append((6, _XS_ANYCAST))
     212        self._connection_queue.append((1, _XS_ANYCAST))
     213        self._try_next_connection_from_queue()
     214