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, 7 months 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  
    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 
     
    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 
     
    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 
     
    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 = '' 
     
    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): 
     
    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 
     
    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' 
     
    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): 
     
    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  
    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 
     
    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): 
     
    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) 
     
    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: 
     
    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', 
     
    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) 
     
    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) 
     
    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' 
     
    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 = '' 
     
    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 
     
    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  
    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  
    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 
     
    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 
     
    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 
     
    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() 
  • (a) /dev/null vs. (b) b/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
    a b  
     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