Ticket #2015: olpc-mesh-0.84.patch

File olpc-mesh-0.84.patch, 33.4 KB (added by erikos, 14 years ago)

Support for olpc-mesh devices for 0.84

  • extensions/deviceicon/network.py

    diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
    index f790c91..bcf0cda 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): 
    385398            self._icon.props.pulsing = True
    386399        elif state == network.DEVICE_STATE_ACTIVATED:
    387400            address = self._device_props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
    388             self._palette.set_connected(self._frequency, address)
     401            self._palette.set_connected_with_frequency(self._frequency, address)
    389402            self._icon.props.pulsing = False
    390403        else:
    391404            self._icon.props.badge_name = None
    class WirelessDeviceView(ToolButton): 
    471484    def __activate_error_cb(self, err):
    472485        logging.debug('Failed to create network: %s', err)
    473486
     487class OlpcMeshDeviceView(ToolButton):
     488    _ICON_NAME = 'network-mesh'
     489    FRAME_POSITION_RELATIVE = 302
     490
     491    def __init__(self, device):
     492        ToolButton.__init__(self)
     493
     494        self._bus = dbus.SystemBus()
     495        self._device = device
     496        self._device_props = None
     497        self._device_state = None
     498        self._channel = 0
     499
     500        self._icon = PulsingIcon(icon_name=self._ICON_NAME)
     501        self._icon.props.pulse_color = xocolor.XoColor( \
     502            "%s,%s" % (style.COLOR_BUTTON_GREY.get_svg(),
     503                       style.COLOR_TRANSPARENT.get_svg()))
     504        self._icon.props.base_color = profile.get_color()
     505
     506        self.set_icon_widget(self._icon)
     507        self._icon.show()
     508
     509        self.set_palette_invoker(FrameWidgetInvoker(self))
     510        self._palette = WirelessPalette(_("Mesh Network"))
     511        self._palette.connect('deactivate-connection',
     512                              self.__deactivate_connection)
     513        self.set_palette(self._palette)
     514        self._palette.set_group_id('frame')
     515
     516        self._device_props = dbus.Interface(self._device,
     517                                            'org.freedesktop.DBus.Properties')
     518        self._device_props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
     519                              reply_handler=self.__get_device_props_reply_cb,
     520                              error_handler=self.__get_device_props_error_cb)
     521        self._device_props.Get(_NM_OLPC_MESH_IFACE, 'ActiveChannel',
     522                            reply_handler=self.__get_active_channel_reply_cb,
     523                            error_handler=self.__get_active_channel_error_cb)
     524
     525        self._bus.add_signal_receiver(self.__state_changed_cb,
     526                                      signal_name='StateChanged',
     527                                      path=self._device.object_path,
     528                                      dbus_interface=_NM_DEVICE_IFACE)
     529        self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
     530                                      signal_name='PropertiesChanged',
     531                                      path=device.object_path,
     532                                      dbus_interface=_NM_OLPC_MESH_IFACE)
     533
     534    def disconnect(self):
     535        self._bus.remove_signal_receiver(self.__state_changed_cb,
     536                                         signal_name='StateChanged',
     537                                         path=self._device.object_path,
     538                                         dbus_interface=_NM_DEVICE_IFACE)
     539        self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
     540                                         signal_name='PropertiesChanged',
     541                                         path=self._device.object_path,
     542                                         dbus_interface=_NM_OLPC_MESH_IFACE)
     543
     544    def __get_device_props_reply_cb(self, properties):
     545        if 'State' in properties:
     546            self._device_state = properties['State']
     547            self._update()
     548
     549    def __get_device_props_error_cb(self, err):
     550        logging.error('Error getting the device properties: %s', err)
     551
     552    def __get_active_channel_reply_cb(self, channel):
     553        self._channel = channel
     554        self._update_text()
     555
     556    def __get_active_channel_error_cb(self, err):
     557        logging.error('Error getting the active channel: %s', err)
     558
     559    def __state_changed_cb(self, new_state, old_state, reason):
     560        self._device_state = new_state
     561        self._update()
     562
     563    def __wireless_properties_changed_cb(self, properties):
     564        if 'ActiveChannel' in properties:
     565            self._channel = properties['ActiveChannel']
     566            self._update_text()
     567
     568    def _update_text(self):
     569        text = _("Mesh Network") + " " + str(self._channel)
     570        self._palette.props.primary_text = text
     571
     572    def _update(self):
     573        state = self._device_state
     574
     575        if state in [network.DEVICE_STATE_PREPARE,
     576                     network.DEVICE_STATE_CONFIG,
     577                     network.DEVICE_STATE_NEED_AUTH,
     578                     network.DEVICE_STATE_IP_CONFIG]:
     579            self._palette.set_connecting()
     580            self._icon.props.pulsing = True
     581        elif state == network.DEVICE_STATE_ACTIVATED:
     582            address = self._device_props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
     583            self._palette.set_connected_with_channel(self._channel, address)
     584            self._icon.props.pulsing = False
     585
     586    def __deactivate_connection(self, palette, data=None):
     587        obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
     588        netmgr = dbus.Interface(obj, _NM_IFACE)
     589        netmgr_props = dbus.Interface(netmgr, 'org.freedesktop.DBus.Properties')
     590        active_connections_o = netmgr_props.Get(_NM_IFACE,
     591                                                'ActiveConnections')
     592
     593        for conn_o in active_connections_o:
     594            # The connection path for a mesh connection is the device itself.
     595            obj = self._bus.get_object(_NM_IFACE, conn_o)
     596            props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
     597            ap_op = props.Get(_NM_ACTIVE_CONN_IFACE, 'SpecificObject')
     598
     599            try:
     600                obj = self._bus.get_object(_NM_IFACE, ap_op)
     601                props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
     602                type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
     603                if type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     604                    netmgr.DeactivateConnection(conn_o)
     605                    break
     606            except dbus.exceptions.DBusException:
     607                pass
     608
    474609class WiredDeviceView(TrayIcon):
    475610
    476611    _ICON_NAME = 'network-wired'
  • src/jarabe/desktop/meshbox.py

    diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
    index 81363ac..9648b4f 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): 
    139143            and self._name[-15] == '#'
    140144
    141145    def _create_palette(self):
    142         icon_name = get_icon_state(_ICON_NAME, self._strength)
     146        icon_name = get_icon_state(_AP_ICON_NAME, self._strength)
    143147        self._palette_icon = Icon(icon_name=icon_name,
    144148                                  icon_size=style.STANDARD_ICON_SIZE,
    145149                                  badge_name=self.props.badge_name)
    class WirelessNetworkView(CanvasPulsingIcon): 
    216220                if self._mode == network.NM_802_11_MODE_INFRA:
    217221                    connection.set_connected()
    218222
    219             icon_name = '%s-connected' % _ICON_NAME
     223            icon_name = '%s-connected' % _AP_ICON_NAME
    220224        else:
    221             icon_name = _ICON_NAME
     225            icon_name = _AP_ICON_NAME
    222226
    223227        icon_name = get_icon_state(icon_name, self._strength)
    224228        if icon_name:
    class WirelessNetworkView(CanvasPulsingIcon): 
    443447            return None
    444448        return self._access_points[ap_path]
    445449
     450    def is_olpc_mesh(self):
     451        return self._mode == network.NM_802_11_MODE_ADHOC \
     452            and self.name == "olpc-mesh"
     453
     454    def remove_all_aps(self):
     455        for ap in self._access_points.values():
     456            ap.disconnect()
     457        self._access_points = {}
     458        self._active_ap = None
     459        self.update_strength()
     460
    446461    def disconnect(self):
    447462        self._bus.remove_signal_receiver(self.__device_state_changed_cb,
    448463                                         signal_name='StateChanged',
    class WirelessNetworkView(CanvasPulsingIcon): 
    454469                                         dbus_interface=_NM_WIRELESS_IFACE)
    455470
    456471
     472class OlpcMeshView(CanvasPulsingIcon):
     473    def __init__(self, mesh_mgr, channel):
     474        CanvasPulsingIcon.__init__(self, icon_name=_OLPC_MESH_ICON_NAME,
     475                                   size=style.STANDARD_ICON_SIZE, cache=True)
     476        self._bus = dbus.SystemBus()
     477        self._channel = channel
     478        self._mesh_mgr = mesh_mgr
     479        self._disconnect_item = None
     480        self._connect_item = None
     481        self._greyed_out = False
     482        self._name = ''
     483        self._device_state = None
     484        self._connection = None
     485        self._active = False
     486        device = mesh_mgr.mesh_device
     487
     488        self.connect('button-release-event', self.__button_release_event_cb)
     489
     490        interface_props = dbus.Interface(device,
     491                                         'org.freedesktop.DBus.Properties')
     492        interface_props.Get(_NM_DEVICE_IFACE, 'State',
     493                            reply_handler=self.__get_device_state_reply_cb,
     494                            error_handler=self.__get_device_state_error_cb)
     495        interface_props.Get(_NM_OLPC_MESH_IFACE, 'ActiveChannel',
     496                            reply_handler=self.__get_active_channel_reply_cb,
     497                            error_handler=self.__get_active_channel_error_cb)
     498
     499        self._bus.add_signal_receiver(self.__device_state_changed_cb,
     500                                      signal_name='StateChanged',
     501                                      path=device.object_path,
     502                                      dbus_interface=_NM_DEVICE_IFACE)
     503        self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
     504                                      signal_name='PropertiesChanged',
     505                                      path=device.object_path,
     506                                      dbus_interface=_NM_OLPC_MESH_IFACE)
     507
     508        pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
     509                                         style.COLOR_TRANSPARENT.get_svg()))
     510        self.props.pulse_color = pulse_color
     511        self.props.base_color = profile.get_color()
     512        self._palette = self._create_palette()
     513        self.set_palette(self._palette)
     514
     515    def _create_palette(self):
     516        _palette = palette.Palette(_("Mesh Network %d") % self._channel)
     517
     518        self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
     519        self._connect_item.connect('activate', self.__connect_activate_cb)
     520        _palette.menu.append(self._connect_item)
     521
     522        return _palette
     523
     524    def __get_device_state_reply_cb(self, state):
     525        self._device_state = state
     526        self._update()
     527
     528    def __get_device_state_error_cb(self, err):
     529        logging.error('Error getting the device state: %s', err)
     530
     531    def __device_state_changed_cb(self, new_state, old_state, reason):
     532        self._device_state = new_state
     533        self._update()
     534
     535    def __get_active_channel_reply_cb(self, channel):
     536        self._active = (channel == self._channel)
     537        self._update()
     538
     539    def __get_active_channel_error_cb(self, err):
     540        logging.error('Error getting the active channel: %s', err)
     541
     542    def __wireless_properties_changed_cb(self, properties):
     543        if 'ActiveChannel' in properties:
     544            channel = properties['ActiveChannel']
     545            self._active = (channel == self._channel)
     546            self._update()
     547
     548    def _update(self):
     549        if self._active:
     550            state = self._device_state
     551        else:
     552            state = network.DEVICE_STATE_UNKNOWN
     553
     554        if state in [network.DEVICE_STATE_PREPARE,
     555                     network.DEVICE_STATE_CONFIG,
     556                     network.DEVICE_STATE_NEED_AUTH,
     557                     network.DEVICE_STATE_IP_CONFIG]:
     558            if self._disconnect_item:
     559                self._disconnect_item.show()
     560            self._connect_item.hide()
     561            self._palette.props.secondary_text = _('Connecting...')
     562            self.props.pulsing = True
     563        elif state == network.DEVICE_STATE_ACTIVATED:
     564            if self._disconnect_item:
     565                self._disconnect_item.show()
     566            self._connect_item.hide()
     567            self._palette.props.secondary_text = _('Connected')
     568            self.props.pulsing = False
     569        else:
     570            if self._disconnect_item:
     571                self._disconnect_item.hide()
     572            self._connect_item.show()
     573            self._palette.props.secondary_text = None
     574            self.props.pulsing = False
     575
     576    def _update_color(self):
     577        if self._greyed_out:
     578            self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
     579        else:
     580            self.props.base_color = profile.get_color()
     581
     582    def __connect_activate_cb(self, icon):
     583        self._connect()
     584
     585    def __button_release_event_cb(self, icon, event):
     586        self._connect()
     587
     588    def _connect(self):
     589        self._mesh_mgr.user_activate_channel(self._channel)
     590
     591    def __activate_reply_cb(self, connection):
     592        logging.debug('Connection activated: %s', connection)
     593
     594    def __activate_error_cb(self, err):
     595        logging.error('Failed to activate connection: %s', err)
     596
     597    def set_filter(self, query):
     598        self._greyed_out = (query != '')
     599        self._update_color()
     600
     601    def disconnect(self):
     602        self._bus.remove_signal_receiver(self.__device_state_changed_cb,
     603                                         signal_name='StateChanged',
     604                                         path=self._device.object_path,
     605                                         dbus_interface=_NM_DEVICE_IFACE)
     606        self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
     607                                         signal_name='PropertiesChanged',
     608                                         path=self._device.object_path,
     609                                         dbus_interface=_NM_OLPC_MESH_IFACE)
     610
     611
    457612class ActivityView(hippo.CanvasBox):
    458613    def __init__(self, model):
    459614        hippo.CanvasBox.__init__(self)
    class NetworkManagerObserver(object): 
    752907        device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
    753908        if device_type == network.DEVICE_TYPE_802_11_WIRELESS:
    754909            self._devices[device_o] = DeviceObserver(self._box, device)
     910        elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     911            self._box.enable_olpc_mesh(device)
    755912
    756913    def _get_device_path_error_cb(self, err):
    757914        logging.error('Failed to get device type: %s', err)
    class NetworkManagerObserver(object): 
    764921            observer = self._devices[device_o]
    765922            observer.disconnect()
    766923            del self._devices[device_o]
     924            return
     925           
     926        device = self._bus.get_object(_NM_SERVICE, device_o)
     927        props = dbus.Interface(device, 'org.freedesktop.DBus.Properties')
     928        device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
     929        if device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
     930            self._box.disable_olpc_mesh(device)
    767931
    768932
    769933class MeshBox(gtk.VBox):
    class MeshBox(gtk.VBox): 
    779943        self._model = neighborhood.get_model()
    780944        self._buddies = {}
    781945        self._activities = {}
    782         self._mesh = {}
     946        self._mesh = []
    783947        self._buddy_to_activity = {}
    784948        self._suspended = True
    785949        self._query = ''
    class MeshBox(gtk.VBox): 
    9241088            del self.wireless_networks[hash]
    9251089 
    9261090    def _ap_props_changed_cb(self, ap, old_hash):
     1091        # if we have mesh hardware, ignore OLPC mesh networks that appear as
     1092        # normal wifi networks
     1093        if len(self._mesh) > 0 and ap.mode == network.NM_802_11_MODE_ADHOC \
     1094                and ap.name == "olpc-mesh":
     1095            logging.debug("ignoring OLPC mesh IBSS")
     1096            ap.disconnect()
     1097            return
     1098
    9271099        if old_hash is None: # new AP finished initializing
    9281100            self._add_ap_to_network(ap)
    9291101            return
    class MeshBox(gtk.VBox): 
    9581130            self._remove_net_if_empty(net, ap.network_hash())
    9591131            return
    9601132
    961         logging.error('Can not remove access point %s', ap_o)
     1133        # it's not an error if the AP isn't found, since we might have ignored
     1134        # it (e.g. olpc-mesh adhoc network)
     1135        logging.debug('Can not remove access point %s' % ap_o)
     1136
     1137    def _add_olpc_mesh_icon(self, mesh_mgr, channel):
     1138        icon = OlpcMeshView(mesh_mgr, channel)
     1139        self._layout.add(icon)
     1140        self._mesh.append(icon)
     1141
     1142    def enable_olpc_mesh(self, mesh_device):
     1143        mesh_mgr = OlpcMeshManager(mesh_device)
     1144        self._add_olpc_mesh_icon(mesh_mgr, 1)
     1145        self._add_olpc_mesh_icon(mesh_mgr, 6)
     1146        self._add_olpc_mesh_icon(mesh_mgr, 11)
     1147
     1148        # the OLPC mesh can be recognised as a "normal" wifi network. remove
     1149        # any such normal networks if they have been created
     1150        for hash, net in self.wireless_networks.iteritems():
     1151            if not net.is_olpc_mesh():
     1152                continue
     1153
     1154            logging.debug("removing OLPC mesh IBSS")
     1155            net.remove_all_aps()
     1156            net.disconnect()
     1157            self._layout.remove(net)
     1158            del self.wireless_networks[hash]
     1159
     1160    def disable_olpc_mesh(self, mesh_device):
     1161        for icon in self._mesh:
     1162            icon.disconnect()
     1163            self._layout.remove(icon)
     1164        self._mesh = []
    9621165
    9631166    def suspend(self):
    9641167        if not self._suspended:
    9651168            self._suspended = True
    966             for net in self.wireless_networks.values():
     1169            for net in self.wireless_networks.values() + self._mesh:
    9671170                net.props.paused = True
    9681171
    9691172    def resume(self):
    9701173        if self._suspended:
    9711174            self._suspended = False
    972             for net in self.wireless_networks.values():
     1175            for net in self.wireless_networks.values() + self._mesh:
    9731176                net.props.paused = False
    9741177
    9751178    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 23b7472..97c5d27 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
     125class OlpcMesh(object):
     126    nm_name = "802-11-olpc-mesh"
     127
     128    def __init__(self, channel, anycast_addr):
     129        self.channel = channel
     130        self.anycast_addr = anycast_addr
     131
     132    def get_dict(self):
     133        ret = {
     134            "ssid": dbus.ByteArray("olpc-mesh"),
     135            "channel": self.channel,
     136        }
     137
     138        if self.anycast_addr:
     139            ret["dhcp-anycast-address"] = dbus.ByteArray(self.anycast_addr)
     140        return ret
     141
    122142class Connection(object):
    123143    def __init__(self):
    124144        self.id = None
    class IP4Config(object): 
    147167        return ip4_config
    148168
    149169class Settings(object):
    150     def __init__(self):
     170    def __init__(self, wireless_cfg=None):
    151171        self.connection = Connection()
    152         self.wireless = Wireless()
    153172        self.ip4_config = None
    154173        self.wireless_security = None
    155174
     175        if wireless_cfg is not None:
     176            self.wireless = wireless_cfg
     177        else:
     178            self.wireless = Wireless()
     179
    156180    def get_dict(self):
    157181        settings = {}
    158182        settings['connection'] = self.connection.get_dict()
    159         settings['802-11-wireless'] = self.wireless.get_dict()
     183        settings[self.wireless.nm_name] = self.wireless.get_dict()
    160184        if self.wireless_security is not None:
    161185            settings['802-11-wireless-security'] = \
    162186                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..bdf0cec
    - +  
     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_timer()
     87        else:
     88            self._start_automesh()
     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