Ticket #230: 0001-Add-OLPC-mesh-support-230.2.patch

File 0001-Add-OLPC-mesh-support-230.2.patch, 35.2 KB (added by dsd, 14 years ago)

updated patch with improved device icon behaviour

  • extensions/deviceicon/network.py

    From 9225172b62f9bc7b1c7e006a23a81383d578d212 Mon Sep 17 00:00:00 2001
    From: Daniel Drake <dsd@laptop.org>
    Date: Thu, 24 Dec 2009 16:28:01 +0000
    Subject: [PATCH] Add OLPC mesh support (#230)
    
    This is supported in NetworkManager-0.8.
    
    The behaviour is designed to mimic XO-1, except it does not look
    for mesh portals other than the school server.
    ---
     extensions/deviceicon/network.py |  164 +++++++++++++++++++++++++--
     src/jarabe/desktop/meshbox.py    |  230 ++++++++++++++++++++++++++++++++++++--
     src/jarabe/model/Makefile.am     |    1 +
     src/jarabe/model/network.py      |   32 +++++-
     src/jarabe/model/olpcmesh.py     |  198 ++++++++++++++++++++++++++++++++
     5 files changed, 600 insertions(+), 25 deletions(-)
     create mode 100644 src/jarabe/model/olpcmesh.py
    
    diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
    index 3c17253..ba72d04 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        self._palette.props.primary_text = _("Mesh Network") + " " + str(self._channel)
     568
     569    def _update(self):
     570        state = self._device_state
     571
     572        if state == network.DEVICE_STATE_PREPARE or \
     573           state == network.DEVICE_STATE_CONFIG or \
     574           state == network.DEVICE_STATE_NEED_AUTH or \
     575           state == network.DEVICE_STATE_IP_CONFIG:
     576            self._palette.set_connecting()
     577            self._icon.props.pulsing = True
     578        elif state == network.DEVICE_STATE_ACTIVATED:
     579            address = self._device_props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
     580            self._palette.set_connected_with_channel(self._channel, address)
     581            self._icon.props.pulsing = False
     582
     583    def __deactivate_connection(self, palette, data=None):
     584        obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
     585        netmgr = dbus.Interface(obj, _NM_IFACE)
     586        netmgr_props = dbus.Interface(
     587            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, view_class=WirelessDeviceView):
    491628        self._device = device
    492629        self._device_view = None
    493630        self._tray = tray
    494631
    495         self._device_view = WirelessDeviceView(self._device)
     632        self._device_view = view_class(self._device)
    496633        self._tray.add_device(self._device_view)
    497634
    498635    def disconnect(self):
    class NetworkManagerObserver(object): 
    593730        elif device_type == network.DEVICE_TYPE_802_11_WIRELESS:
    594731            device = WirelessDeviceObserver(nm_device, self._tray)
    595732            self._devices[device_op] = device
     733        elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     734            device = WirelessDeviceObserver(nm_device, self._tray, view_class=OlpcMeshDeviceView)
     735            self._devices[device_op] = device
    596736
    597737    def __device_added_cb(self, device_op):
    598738        self._check_device(device_op)
  • src/jarabe/desktop/meshbox.py

    diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
    index 76880b8..70c836a 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.meshdev
     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        p = palette.Palette(_("Mesh Network") + " " + str(self._channel))
     493
     494        self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
     495        self._connect_item.connect('activate', self.__connect_activate_cb)
     496        p.menu.append(self._connect_item)
     497
     498        self._disconnect_item = MenuItem(_('Disconnect'), 'media-eject')
     499        self._disconnect_item.connect('activate',
     500                                      self._disconnect_activate_cb)
     501        p.menu.append(self._disconnect_item)
     502
     503        return p
     504
     505    def __get_device_state_reply_cb(self, state):
     506        self._device_state = state
     507        self._update()
     508
     509    def __get_device_state_error_cb(self, err):
     510        logging.error('Error getting the device state: %s', err)
     511
     512    def __device_state_changed_cb(self, new_state, old_state, reason):
     513        self._device_state = new_state
     514        self._update()
     515
     516    def __get_active_channel_reply_cb(self, channel):
     517        self._active = (channel == self._channel)
     518        self._update()
     519
     520    def __get_active_channel_error_cb(self, err):
     521        logging.error('Error getting the active channel: %s', err)
     522
     523    def __wireless_properties_changed_cb(self, properties):
     524        if 'ActiveChannel' in properties:
     525            channel = properties['ActiveChannel']
     526            self._active = (channel == self._channel)
     527            self._update()
     528
     529    def _update(self):
     530        if self._active:
     531            state = self._device_state
     532        else:
     533            state = network.DEVICE_STATE_UNKNOWN
     534
     535        if state == network.DEVICE_STATE_PREPARE or \
     536           state == network.DEVICE_STATE_CONFIG or \
     537           state == network.DEVICE_STATE_NEED_AUTH or \
     538           state == network.DEVICE_STATE_IP_CONFIG:
     539            if self._disconnect_item:
     540                self._disconnect_item.show()
     541            self._connect_item.hide()
     542            self._palette.props.secondary_text = _('Connecting...')
     543            self.props.pulsing = True
     544        elif state == network.DEVICE_STATE_ACTIVATED:
     545            if self._disconnect_item:
     546                self._disconnect_item.show()
     547            self._connect_item.hide()
     548            self._palette.props.secondary_text = _('Connected')
     549            self.props.pulsing = False
     550        else:
     551            if self._disconnect_item:
     552                self._disconnect_item.hide()
     553            self._connect_item.show()
     554            self._palette.props.secondary_text = None
     555            self.props.pulsing = False
     556
     557    def _update_color(self):
     558        if self._greyed_out:
     559            self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
     560        else:
     561            self.props.base_color = profile.get_color()
     562
     563    def _disconnect_activate_cb(self, item):
     564        pass
     565
     566    def __connect_activate_cb(self, icon):
     567        self._connect()
     568
     569    def __button_release_event_cb(self, icon, event):
     570        self._connect()
     571
     572    def _connect(self):
     573        self._mesh_mgr.user_activate_channel(self._channel)
     574
     575    def __activate_reply_cb(self, connection):
     576        logging.debug('Connection activated: %s', connection)
     577
     578    def __activate_error_cb(self, err):
     579        logging.error('Failed to activate connection: %s', err)
     580
     581    def set_filter(self, query):
     582        self._greyed_out = (query != '')
     583        self._update_color()
     584
     585    def disconnect(self):
     586        self._bus.remove_signal_receiver(self.__device_state_changed_cb,
     587                                         signal_name='StateChanged',
     588                                         path=self._device.object_path,
     589                                         dbus_interface=_NM_DEVICE_IFACE)
     590        self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
     591                                         signal_name='PropertiesChanged',
     592                                         path=self._device.object_path,
     593                                         dbus_interface=_NM_OLPC_MESH_IFACE)
     594
     595
    433596class ActivityView(hippo.CanvasBox):
    434597    def __init__(self, model):
    435598        hippo.CanvasBox.__init__(self)
    class NetworkManagerObserver(object): 
    729892        device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
    730893        if device_type == network.DEVICE_TYPE_802_11_WIRELESS:
    731894            self._devices[device_o] = DeviceObserver(self._box, device)
     895        elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     896            self._box.enable_olpc_mesh(device)
    732897
    733898    def _get_device_path_error_cb(self, err):
    734899        logging.error('Failed to get device type: %s', err)
    class NetworkManagerObserver(object): 
    741906            observer = self._devices[device_o]
    742907            observer.disconnect()
    743908            del self._devices[device_o]
    744 
     909            return
     910           
     911        device = self._bus.get_object(_NM_SERVICE, device_o)
     912        props = dbus.Interface(device, 'org.freedesktop.DBus.Properties')
     913        device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
     914        if device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     915            self._box.disable_olpc_mesh(device)
    745916
    746917class MeshBox(gtk.VBox):
    747918    __gtype_name__ = 'SugarMeshBox'
    class MeshBox(gtk.VBox): 
    756927        self._model = neighborhood.get_model()
    757928        self._buddies = {}
    758929        self._activities = {}
    759         self._mesh = {}
     930        self._mesh = []
    760931        self._buddy_to_activity = {}
    761932        self._suspended = True
    762933        self._query = ''
    class MeshBox(gtk.VBox): 
    9011072            del self.wireless_networks[hash]
    9021073
    9031074    def _ap_props_changed_cb(self, ap, old_hash):
     1075        # if we have mesh hardware, ignore OLPC mesh networks that appear as
     1076        # normal wifi networks
     1077        if len(self._mesh) > 0 and ap.mode == network.NM_802_11_MODE_ADHOC \
     1078                and ap.name == "olpc-mesh":
     1079            logging.debug("ignoring OLPC mesh IBSS")
     1080            ap.disconnect()
     1081            return
     1082
    9041083        if old_hash is None: # new AP finished initializing
    9051084            self._add_ap_to_network(ap)
    9061085            return
    class MeshBox(gtk.VBox): 
    9351114            self._remove_net_if_empty(net, ap.network_hash())
    9361115            return
    9371116
    938         logging.error('Can not remove access point %s', ap_o)
     1117        # it's not an error if the AP isn't found, since we might have ignored
     1118        # it (e.g. olpc-mesh adhoc network)
     1119        logging.debug('Can not remove access point %s' % ap_o)
     1120
     1121    def _add_olpc_mesh_icon(self, mesh_mgr, channel):
     1122        icon = OlpcMeshView(mesh_mgr, channel)
     1123        self._layout.add(icon)
     1124        self._mesh.append(icon)
     1125
     1126    def enable_olpc_mesh(self, meshdev):
     1127        mesh_mgr = OlpcMeshManager(meshdev)
     1128        self._add_olpc_mesh_icon(mesh_mgr, 1)
     1129        self._add_olpc_mesh_icon(mesh_mgr, 6)
     1130        self._add_olpc_mesh_icon(mesh_mgr, 11)
     1131
     1132        # the OLPC mesh can be recognised as a "normal" wifi network. remove
     1133        # any such normal networks if they have been created
     1134        for hash, net in self.wireless_networks.iteritems():
     1135            if not net.is_olpc_mesh():
     1136                continue
     1137
     1138            logging.debug("removing OLPC mesh IBSS")
     1139            net.remove_all_aps()
     1140            net.disconnect()
     1141            self._layout.remove(net)
     1142            del self.wireless_networks[hash]
     1143
     1144    def disable_olpc_mesh(self, meshdev):
     1145        for icon in self._mesh:
     1146            icon.disconnect()
     1147            self._layout.remove(icon)
     1148        self._mesh = []
    9391149
    9401150    def suspend(self):
    9411151        if not self._suspended:
    9421152            self._suspended = True
    943             for net in self.wireless_networks.values():
     1153            for net in self.wireless_networks.values() + self._mesh:
    9441154                net.props.paused = True
    9451155
    9461156    def resume(self):
    9471157        if self._suspended:
    9481158            self._suspended = False
    949             for net in self.wireless_networks.values():
     1159            for net in self.wireless_networks.values() + self._mesh:
    9501160                net.props.paused = False
    9511161
    9521162    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..26c8b53
    - +  
     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, meshdev):
     48        self._bus = dbus.SystemBus()
     49        self.meshdev = meshdev
     50
     51        # let's make sure we know the companion device early on
     52        # (synchronous request)
     53        props = dbus.Interface(meshdev, 'org.freedesktop.DBus.Properties')
     54        ethdev_o = props.Get(_NM_OLPC_MESH_IFACE, 'Companion')
     55        self.ethdev = self._bus.get_object(_NM_SERVICE, ethdev_o)
     56
     57        # asynchronously learn about mesh status
     58        props.Get(_NM_DEVICE_IFACE, 'State',
     59                  reply_handler=self.__get_mesh_state_reply_cb,
     60                  error_handler=self.__get_state_error_cb)
     61        # and companion
     62        props = dbus.Interface(self.ethdev, 'org.freedesktop.DBus.Properties')
     63        props.Get(_NM_DEVICE_IFACE, 'State',
     64                  reply_handler=self.__get_eth_state_reply_cb,
     65                  error_handler=self.__get_state_error_cb)
     66
     67        # this is a stack of connections that we'll iterate through until
     68        # we find one that works
     69        self._connection_queue = []
     70
     71        self._bus.add_signal_receiver(self.__ethdev_state_changed_cb,
     72                                      signal_name='StateChanged',
     73                                      path=self.ethdev.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.meshdev.object_path,
     79                                      dbus_interface=_NM_DEVICE_IFACE)
     80
     81        self._idle_source = 0
     82        self._meshdev_state = DEVICE_STATE_UNKNOWN
     83        self._ethdev_state = DEVICE_STATE_UNKNOWN
     84
     85        if len(network.get_settings().connections) == 0:
     86            # if there are no configured APs, start meshing immediately
     87            self.start_automesh()
     88        else:
     89            # otherwise, start our timer system which basically looks for 10
     90            # seconds of inactivity on both devices, then starts automesh
     91            self._idle_source = gobject.timeout_add_seconds(10, self._idle_check)
     92
     93    def __get_state_error_cb(self, err):
     94        logging.debug('Error getting the device state: %s', err)
     95
     96    def __get_mesh_state_reply_cb(self, state):
     97        self._meshdev_state = state
     98        self._maybe_schedule_idle_check()
     99
     100    def __get_eth_state_reply_cb(self, state):
     101        self._ethdev_state = state
     102        self._maybe_schedule_idle_check()
     103
     104    def __ethdev_state_changed_cb(self, new_state, old_state, reason):
     105        self._ethdev_state = new_state
     106        self._maybe_schedule_idle_check()
     107
     108        # If a connection is activated on the eth device, stop trying our
     109        # automatic connections
     110        if new_state >= DEVICE_STATE_PREPARE \
     111                and new_state <= DEVICE_STATE_ACTIVATED \
     112                and len(self._connection_queue) > 0:
     113            self._connection_queue = []
     114
     115    def __mshdev_state_changed_cb(self, new_state, old_state, reason):
     116        self._meshdev_state = new_state
     117        self._maybe_schedule_idle_check()
     118
     119        if new_state == DEVICE_STATE_FAILED:
     120            # try next connection in queue
     121            self._activate_from_queue()
     122        elif new_state == DEVICE_STATE_ACTIVATED \
     123                and len(self._connection_queue) > 0:
     124            # clear connection queue, we were successful
     125            self._connection_queue = []
     126
     127    def _maybe_schedule_idle_check(self):
     128        # if both devices are disconnected, wait 10 seconds for some activity
     129        # and if nothing happens then we can start automatic meshing
     130        if self._meshdev_state == DEVICE_STATE_DISCONNECTED \
     131                and self._ethdev_state == DEVICE_STATE_DISCONNECTED:
     132            if self._idle_source != 0:
     133                gobject.source_remove(self._idle_source)
     134            self._idle_source = gobject.timeout_add_seconds(10, self._idle_check)
     135
     136    def _idle_check(self):
     137        if self._meshdev_state == DEVICE_STATE_DISCONNECTED \
     138                and self._ethdev_state == DEVICE_STATE_DISCONNECTED:
     139            logging.debug("starting automesh due to inactivity")
     140            self.start_automesh()
     141        return False
     142
     143    def _make_connection(self, channel, anycast_addr=None):
     144        settings = Settings(wireless_cfg=OlpcMeshSettings(channel, anycast_addr))
     145        if not anycast_addr:
     146            settings.ip4_config = network.IP4Config()
     147            settings.ip4_config.method = 'link-local'
     148        settings.connection.id = 'olpc-mesh-' + str(channel)
     149        settings.connection.uuid = unique_id()
     150        settings.connection.type = '802-11-olpc-mesh'
     151        connection = network.add_connection(settings.connection.id, settings)
     152        return connection
     153
     154    def __activate_reply_cb(self, connection):
     155        logging.debug('Connection activated: %s', connection)
     156
     157    def __activate_error_cb(self, err):
     158        logging.error('Failed to activate connection: %s', err)
     159
     160    def activate_connection(self, channel, anycast_addr=None):
     161        logging.debug("activate channel %d anycast %s" % (channel, repr(anycast_addr)))
     162        obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
     163        netmgr = dbus.Interface(obj, _NM_IFACE)
     164        connection = self._make_connection(channel, anycast_addr)
     165
     166        netmgr.ActivateConnection(network.SETTINGS_SERVICE, connection.path,
     167                                  self.meshdev.object_path,
     168                                  self.meshdev.object_path,
     169                                  reply_handler=self.__activate_reply_cb,
     170                                  error_handler=self.__activate_error_cb)
     171
     172    def _activate_from_queue(self):
     173        if len(self._connection_queue) == 0:
     174            return
     175
     176        channel, anycast = self._connection_queue.pop()
     177        self.activate_connection(channel, anycast)
     178
     179    # Activate a mesh connection on a user-specified channel
     180    # Looks for XS first, then resorts to simple mesh
     181    def user_activate_channel(self, channel):
     182        # Empties any existing queue in order to abort any connection attempt
     183        # that was already happening
     184        self._connection_queue = [ (channel, None), (channel, _XS_ANYCAST) ]
     185        self._activate_from_queue()
     186
     187    # Start meshing, intended for when there are no better networks to
     188    # connect to. First looks for XS on all channels, then falls back to
     189    # simple mesh on channel 1.
     190    def start_automesh(self):
     191        # Empties any existing queue in order to abort any connection attempt
     192        # that was already happening
     193        self._connection_queue = [
     194            (1, None),
     195            (11, _XS_ANYCAST), (6, _XS_ANYCAST), (1, _XS_ANYCAST)
     196        ]
     197        self._activate_from_queue()
     198