Ticket #1610: 0001-Add-default-Ad-hoc-networks-1610.3.patch

File 0001-Add-default-Ad-hoc-networks-1610.3.patch, 43.0 KB (added by erikos, 12 years ago)

New patch which addresses the review comments and uses a gconf option to determine whether to show the sugar adhoc networks instead of checking for the mesh device

  • data/sugar.schemas.in

    From 5a8b4f141fc9fc04b620e7609a0a0c49d64fc735 Mon Sep 17 00:00:00 2001
    From: Simon Schampijer <simon@schampijer.de>
    Date: Wed, 11 Aug 2010 16:08:51 +0200
    Subject: [PATCH] Add default Ad-hoc networks #1610
    
    This patch adds three default Ad-hoc networks, for channel 1, 6 and 11. They
    are represented with designated icons in the neighborhood view. This will
    mimic the mesh behavior on devices where mesh hardware is not available
    and make the "under a tree"-scenario possible in those cases. If Sugar sees
    no "known" network when it starts, it does autoconnect to an Ad-hoc network.
    ---
     data/sugar.schemas.in            |   12 ++
     extensions/deviceicon/network.py |  116 ++++------------
     src/jarabe/desktop/meshbox.py    |  297 +++++++++++++++++++++++++++++++++-----
     src/jarabe/model/Makefile.am     |    1 +
     src/jarabe/model/adhoc.py        |  273 +++++++++++++++++++++++++++++++++++
     src/jarabe/model/network.py      |   24 +++-
     6 files changed, 593 insertions(+), 130 deletions(-)
     create mode 100644 src/jarabe/model/adhoc.py
    
    diff --git a/data/sugar.schemas.in b/data/sugar.schemas.in
    index b9606ba..65f4205 100644
    a b  
    329329      </locale>
    330330    </schema>
    331331
     332    <schema>
     333      <key>/schemas/desktop/sugar/network/adhoc</key>
     334      <applyto>/desktop/sugar/network/adhoc</applyto>
     335      <owner>sugar</owner>
     336      <type>bool</type>
     337      <default>true</default>
     338      <locale name="C">
     339        <short>Show Sugar Ad-hoc networks</short>
     340        <long>>If TRUE, Sugar will show default Ad-hoc networks for channel 1,6 and 11</long>
     341      </locale>
     342    </schema>
     343
    332344  </schemalist>
    333345</gconfschemafile>
  • extensions/deviceicon/network.py

    diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
    index 227ba46..0ab4387 100644
    a b _GSM_STATE_CONNECTING = 2 
    6767_GSM_STATE_CONNECTED = 3
    6868_GSM_STATE_NEED_AUTH = 4
    6969
    70 def frequency_to_channel(frequency):
    71     ftoc = { 2412: 1, 2417: 2, 2422: 3, 2427: 4,
    72              2432: 5, 2437: 6, 2442: 7, 2447: 8,
    73              2452: 9, 2457: 10, 2462: 11, 2467: 12,
    74              2472: 13}
    75     return ftoc[frequency]
    7670
    7771class WirelessPalette(Palette):
    7872    __gtype_name__ = 'SugarWirelessPalette'
    7973
    8074    __gsignals__ = {
    8175        'deactivate-connection' : (gobject.SIGNAL_RUN_FIRST,
    82                                    gobject.TYPE_NONE, ([])),
    83         'create-connection'     : (gobject.SIGNAL_RUN_FIRST,
    84                                    gobject.TYPE_NONE, ([])),
     76                                   gobject.TYPE_NONE, ([]))
    8577    }
    8678
    87     def __init__(self, primary_text, can_create=True):
     79    def __init__(self, primary_text):
    8880        Palette.__init__(self, label=primary_text)
    8981
    9082        self._disconnect_item = None
    class WirelessPalette(Palette): 
    117109        self._disconnect_item.connect('activate', self.__disconnect_activate_cb)
    118110        self.menu.append(self._disconnect_item)
    119111
    120         if can_create:
    121             self._adhoc_item = gtk.MenuItem(_('Create new wireless network'))
    122             self._adhoc_item.connect('activate', self.__adhoc_activate_cb)
    123             self.menu.append(self._adhoc_item)
    124             self._adhoc_item.show()
    125 
    126112    def set_connecting(self):
    127113        self.props.secondary_text = _('Connecting...')
    128114
    class WirelessPalette(Palette): 
    149135    def __disconnect_activate_cb(self, menuitem):
    150136        self.emit('deactivate-connection')
    151137
    152     def __adhoc_activate_cb(self, menuitem):
    153         self.emit('create-connection')
    154 
    155138    def _set_frequency(self, frequency):
    156139        try:
    157             channel = frequency_to_channel(frequency)
     140            channel = network.frequency_to_channel(frequency)
    158141        except KeyError:
    159142            channel = 0
    160143        self._set_channel(channel)
    class GsmPalette(Palette): 
    318301
    319302class WirelessDeviceView(ToolButton):
    320303
    321     _ICON_NAME = 'network-wireless'
    322304    FRAME_POSITION_RELATIVE = 302
    323305
    324306    def __init__(self, device):
    class WirelessDeviceView(ToolButton): 
    337319        self._active_ap_op = None
    338320
    339321        self._icon = PulsingIcon()
    340         self._icon.props.icon_name = get_icon_state(self._ICON_NAME, 0)
     322        self._icon.props.icon_name = get_icon_state('network-wireless', 0)
    341323        self._inactive_color = xocolor.XoColor( \
    342324            "%s,%s" % (style.COLOR_BUTTON_GREY.get_svg(),
    343325                       style.COLOR_TRANSPARENT.get_svg()))
    class WirelessDeviceView(ToolButton): 
    351333        self._palette = WirelessPalette(self._name)
    352334        self._palette.connect('deactivate-connection',
    353335                              self.__deactivate_connection_cb)
    354         self._palette.connect('create-connection',
    355                               self.__create_connection_cb)
    356336        self.set_palette(self._palette)
    357337        self._palette.set_group_id('frame')
    358338
    class WirelessDeviceView(ToolButton): 
    422402    def __ap_properties_changed_cb(self, properties):
    423403        self._update_properties(properties)
    424404
    425     def _name_encodes_colors(self):
    426         """Match #XXXXXX,#YYYYYY at the end of the network name"""
    427         return self._name[-7] == '#' and self._name[-8] == ',' \
    428             and self._name[-15] == '#'
    429 
    430405    def _update_properties(self, properties):
    431406        if 'Mode' in properties:
    432407            self._mode = properties['Mode']
    class WirelessDeviceView(ToolButton): 
    442417            self._frequency = properties['Frequency']
    443418
    444419        if self._color == None:
    445             if self._mode == network.NM_802_11_MODE_ADHOC \
    446                     and self._name_encodes_colors():
    447                 encoded_color = self._name.split("#", 1)
    448                 if len(encoded_color) == 2:
    449                     self._color = xocolor.XoColor('#' + encoded_color[1])
     420            if self._mode == network.NM_802_11_MODE_ADHOC and \
     421                    network.is_sugar_adhoc_network(self._name):
     422                self._color = profile.get_color()
    450423            else:
    451424                sha_hash = hashlib.sha1()
    452425                data = self._name + hex(self._flags)
    class WirelessDeviceView(ToolButton): 
    482455        else:
    483456            state = network.DEVICE_STATE_UNKNOWN
    484457
    485         if state == network.DEVICE_STATE_ACTIVATED:
    486             icon_name = '%s-connected' % self._ICON_NAME
    487         else:
    488             icon_name = self._ICON_NAME
     458        if self._mode != network.NM_802_11_MODE_ADHOC and \
     459                network.is_sugar_adhoc_network(self._name) == False:
     460            if state == network.DEVICE_STATE_ACTIVATED:
     461                icon_name = '%s-connected' % 'network-wireless'
     462            else:
     463                icon_name = 'network-wireless'
    489464
    490         icon_name = get_icon_state(icon_name, self._strength)
    491         if icon_name:
    492             self._icon.props.icon_name = icon_name
     465            icon_name = get_icon_state(icon_name, self._strength)
     466            if icon_name:
     467                self._icon.props.icon_name = icon_name
     468        else:
     469            try:
     470                channel = network.frequency_to_channel(self._frequency)
     471            except KeyError:
     472                channel = 1
     473            if state == network.DEVICE_STATE_ACTIVATED:
     474                self._icon.props.icon_name = 'network-adhoc-%s-connected' \
     475                        % channel
     476            else:
     477                self._icon.props.icon_name = 'network-adhoc-%s' % channel
     478            self._icon.props.base_color = profile.get_color()
    493479
    494480        if state == network.DEVICE_STATE_PREPARE or \
    495481           state == network.DEVICE_STATE_CONFIG or \
    class WirelessDeviceView(ToolButton): 
    528514                    netmgr.DeactivateConnection(conn_o)
    529515                    break
    530516
    531     def __create_connection_cb(self, palette, data=None):
    532         """Create an 802.11 IBSS network.
    533 
    534         The user's color is encoded at the end of the network name. The network
    535         name is truncated so that it does not exceed the 32 byte SSID limit.
    536         """
    537         client = gconf.client_get_default()
    538         nick = client.get_string('/desktop/sugar/user/nick').decode('utf-8')
    539         color = client.get_string('/desktop/sugar/user/color')
    540         color_suffix = ' %s' % color
    541 
    542         format = _('%s\'s network').encode('utf-8')
    543         extra_length = (len(format) - len('%s')) + len(color_suffix)
    544         name_limit = 32 - extra_length
    545 
    546         # truncate the nick and use a regex to drop any partial characters
    547         # at the end
    548         nick = nick.encode('utf-8')[:name_limit]
    549         pattern = "([\xf6-\xf7][\x80-\xbf]{0,2}|[\xe0-\xef][\x80-\xbf]{0,1}|[\xc0-\xdf])$"
    550         nick = re.sub(pattern, '', nick)
    551 
    552         connection_name = format % nick
    553         connection_name += color_suffix
    554 
    555         connection = network.find_connection_by_ssid(connection_name)
    556         if connection is None:
    557             settings = Settings()
    558             settings.connection.id = 'Auto ' + connection_name
    559             uuid = settings.connection.uuid = unique_id()
    560             settings.connection.type = '802-11-wireless'
    561             settings.wireless.ssid = dbus.ByteArray(connection_name)
    562             settings.wireless.band = 'bg'
    563             settings.wireless.mode = 'adhoc'
    564             settings.ip4_config = IP4Config()
    565             settings.ip4_config.method = 'link-local'
    566 
    567             connection = network.add_connection(uuid, settings)
    568 
    569         obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
    570         netmgr = dbus.Interface(obj, _NM_IFACE)
    571 
    572         netmgr.ActivateConnection(network.SETTINGS_SERVICE,
    573                                   connection.path,
    574                                   self._device.object_path,
    575                                   '/',
    576                                   reply_handler=self.__activate_reply_cb,
    577                                   error_handler=self.__activate_error_cb)
    578 
    579517    def __activate_reply_cb(self, connection):
    580518        logging.debug('Network created: %s', connection)
    581519
  • src/jarabe/desktop/meshbox.py

    diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
    index e5ef720..d7cdb15 100644
    a b  
    11# Copyright (C) 2006-2007 Red Hat, Inc.
    22# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
    3 # Copyright (C) 2009 One Laptop per Child
     3# Copyright (C) 2009-2010 One Laptop per Child
    44#
    55# This program is free software; you can redistribute it and/or modify
    66# it under the terms of the GNU General Public License as published by
    import dbus 
    2424import hippo
    2525import gobject
    2626import gtk
     27import gconf
    2728
    2829from sugar.graphics.icon import CanvasIcon, Icon
    2930from sugar.graphics.xocolor import XoColor
    from jarabe.model.network import IP4Config 
    5253from jarabe.model.network import WirelessSecurity
    5354from jarabe.model.network import AccessPoint
    5455from jarabe.model.olpcmesh import OlpcMeshManager
     56from jarabe.model import adhoc
    5557from jarabe.journal import misc
    5658
    5759_NM_SERVICE = 'org.freedesktop.NetworkManager'
    _NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active' 
    6668_AP_ICON_NAME = 'network-wireless'
    6769_OLPC_MESH_ICON_NAME = 'network-mesh'
    6870
     71
    6972class WirelessNetworkView(CanvasPulsingIcon):
    7073    def __init__(self, initial_ap):
    7174        CanvasPulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE,
    class WirelessNetworkView(CanvasPulsingIcon): 
    8992        self._connection = None
    9093        self._color = None
    9194
    92         if self._mode == network.NM_802_11_MODE_ADHOC \
    93                 and self._name_encodes_colors():
    94             encoded_color = self._name.split("#", 1)
    95             if len(encoded_color) == 2:
    96                 self._color = xocolor.XoColor('#' + encoded_color[1])
     95        if self._mode == network.NM_802_11_MODE_ADHOC and \
     96                network.is_sugar_adhoc_network(self._name):
     97            self._color = profile.get_color()
    9798        else:
    9899            sha_hash = hashlib.sha1()
    99100            data = self._name + hex(self._flags)
    class WirelessNetworkView(CanvasPulsingIcon): 
    115116        self.set_palette(self._palette)
    116117        self._palette_icon.props.xo_color = self._color
    117118
    118         if network.find_connection_by_ssid(self._name) is not None:
    119             self.props.badge_name = "emblem-favorite"
    120             self._palette_icon.props.badge_name = "emblem-favorite"
    121         elif initial_ap.flags == network.NM_802_11_AP_FLAGS_PRIVACY:
    122             self.props.badge_name = "emblem-locked"
    123             self._palette_icon.props.badge_name = "emblem-locked"
     119        if self._mode != network.NM_802_11_MODE_ADHOC:
     120            if network.find_connection_by_ssid(self._name) is not None:
     121                self.props.badge_name = "emblem-favorite"
     122                self._palette_icon.props.badge_name = "emblem-favorite"
     123            elif self._flags == network.NM_802_11_AP_FLAGS_PRIVACY:
     124                self.props.badge_name = "emblem-locked"
     125                self._palette_icon.props.badge_name = "emblem-locked"
     126            else:
     127                self.props.badge_name = None
     128                self._palette_icon.props.badge_name = None
    124129        else:
    125130            self.props.badge_name = None
    126131            self._palette_icon.props.badge_name = None
    class WirelessNetworkView(CanvasPulsingIcon): 
    146151                                      path=self._device.object_path,
    147152                                      dbus_interface=_NM_WIRELESS_IFACE)
    148153
    149     def _name_encodes_colors(self):
    150         """Match #XXXXXX,#YYYYYY at the end of the network name"""
    151         return self._name[-7] == '#' and self._name[-8] == ',' \
    152             and self._name[-15] == '#'
    153 
    154154    def _create_palette(self):
    155155        icon_name = get_icon_state(_AP_ICON_NAME, self._strength)
    156156        self._palette_icon = Icon(icon_name=icon_name,
    class WirelessNetworkView(CanvasPulsingIcon): 
    221221        else:
    222222            state = network.DEVICE_STATE_UNKNOWN
    223223
    224         if state == network.DEVICE_STATE_ACTIVATED:
    225             connection = network.find_connection_by_ssid(self._name)
    226             if connection:
    227                 if self._mode == network.NM_802_11_MODE_INFRA:
    228                     connection.set_connected()
    229 
    230             icon_name = '%s-connected' % _AP_ICON_NAME
    231         else:
    232             icon_name = _AP_ICON_NAME
    233 
    234         icon_name = get_icon_state(icon_name, self._strength)
    235         if icon_name:
     224        if self._mode == network.NM_802_11_MODE_ADHOC and \
     225                network.is_sugar_adhoc_network(self._name):
     226            channel = max([1] + [ap.channel for ap in
     227                                 self._access_points.values()])
     228            if state == network.DEVICE_STATE_ACTIVATED:
     229                icon_name = 'network-adhoc-%s-connected' % channel
     230            else:
     231                icon_name = 'network-adhoc-%s' % channel
    236232            self.props.icon_name = icon_name
    237233            icon = self._palette.props.icon
    238234            icon.props.icon_name = icon_name
     235        else:
     236            if state == network.DEVICE_STATE_ACTIVATED:
     237                connection = network.find_connection_by_ssid(self._name)
     238                if connection:
     239                    if self._mode == network.NM_802_11_MODE_INFRA:
     240                        connection.set_connected()
     241                icon_name = '%s-connected' % _AP_ICON_NAME
     242            else:
     243                icon_name = _AP_ICON_NAME
     244
     245            icon_name = get_icon_state(icon_name, self._strength)
     246            if icon_name:
     247                self.props.icon_name = icon_name
     248                icon = self._palette.props.icon
     249                icon.props.icon_name = icon_name
    239250
    240251        if state == network.DEVICE_STATE_PREPARE or \
    241252           state == network.DEVICE_STATE_CONFIG or \
    class WirelessNetworkView(CanvasPulsingIcon): 
    443454                                         path=self._device.object_path,
    444455                                         dbus_interface=_NM_WIRELESS_IFACE)
    445456
     457class SugarAdhocView(CanvasPulsingIcon):
     458    """To mimic the mesh behavior on devices where mesh hardware is
     459    not available we support the creation of an Ad-hoc network on
     460    three channels 1, 6, 11. This is the class for an icon
     461    representing a channel in the neighborhood view.
     462
     463    """
     464
     465    _ICON_NAME = 'network-adhoc-'
     466    _NAME = 'Ad-hoc Network '
     467
     468    def __init__(self, channel):
     469        CanvasPulsingIcon.__init__(self,
     470                                   icon_name=self._ICON_NAME + str(channel),
     471                                   size=style.STANDARD_ICON_SIZE, cache=True)
     472        self._bus = dbus.SystemBus()
     473        self._manager = adhoc.get_manager()
     474        self._channel = channel
     475        self._disconnect_item = None
     476        self._connect_item = None
     477        self._palette_icon = None
     478        self._greyed_out = False
     479
     480        self._manager.connect('members-changed', self.__members_changed_cb)
     481        self._manager.connect('state-changed', self.__state_changed_cb)
     482
     483        self.connect('button-release-event', self.__button_release_event_cb)
     484
     485        pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
     486                                         style.COLOR_TRANSPARENT.get_svg()))
     487        self.props.pulse_color = pulse_color
     488        self._state_color = XoColor('%s,%s' % \
     489                                       (profile.get_color().get_stroke_color(),
     490                                        style.COLOR_TRANSPARENT.get_svg()))
     491        self.props.base_color = self._state_color
     492        self._palette = self._create_palette()
     493        self.set_palette(self._palette)
     494        self._palette_icon.props.xo_color = self._state_color
     495
     496    def _create_palette(self):
     497        self._palette_icon = Icon( \
     498                icon_name=self._ICON_NAME + str(self._channel),
     499                icon_size=style.STANDARD_ICON_SIZE)
     500
     501        palette_ = palette.Palette(_("Ad-hoc Network %d") % self._channel,
     502                                   icon=self._palette_icon)
     503
     504        self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
     505        self._connect_item.connect('activate', self.__connect_activate_cb)
     506        palette_.menu.append(self._connect_item)
     507
     508        self._disconnect_item = MenuItem(_('Disconnect'), 'media-eject')
     509        self._disconnect_item.connect('activate',
     510                                      self.__disconnect_activate_cb)
     511        palette_.menu.append(self._disconnect_item)
     512
     513        return palette_
     514
     515    def __button_release_event_cb(self, icon, event):
     516        self._manager.activate_channel(self._channel)
     517
     518    def __connect_activate_cb(self, icon):
     519        self._manager.activate_channel(self._channel)
     520
     521    def __disconnect_activate_cb(self, icon):
     522        self._manager.deactivate_active_channel()
     523
     524    def __state_changed_cb(self, adhoc_manager, channel, device_state):
     525        if self._channel == channel:
     526            state = device_state
     527        else:
     528            state = network.DEVICE_STATE_UNKNOWN
     529
     530        if state == network.DEVICE_STATE_ACTIVATED:
     531            icon_name = '%s-connected' % self._ICON_NAME + str(self._channel)
     532        else:
     533            icon_name = self._ICON_NAME + str(self._channel)
     534
     535        self.props.base_color = self._state_color
     536        self._palette_icon.props.xo_color = self._state_color
     537
     538        if icon_name is not None:
     539            self.props.icon_name = icon_name
     540            icon = self._palette.props.icon
     541            icon.props.icon_name = icon_name
     542
     543        if state in [network.DEVICE_STATE_PREPARE,
     544                     network.DEVICE_STATE_CONFIG,
     545                     network.DEVICE_STATE_NEED_AUTH,
     546                     network.DEVICE_STATE_IP_CONFIG]:
     547            if self._disconnect_item:
     548                self._disconnect_item.show()
     549            self._connect_item.hide()
     550            self._palette.props.secondary_text = _('Connecting...')
     551            self.props.pulsing = True
     552        elif state == network.DEVICE_STATE_ACTIVATED:
     553            if self._disconnect_item:
     554                self._disconnect_item.show()
     555            self._connect_item.hide()
     556            self._palette.props.secondary_text = _('Connected')
     557            self.props.pulsing = False
     558        else:
     559            if self._disconnect_item:
     560                self._disconnect_item.hide()
     561            self._connect_item.show()
     562            self._palette.props.secondary_text = None
     563            self.props.pulsing = False
     564
     565    def _update_color(self):
     566        if self._greyed_out:
     567            self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
     568        else:
     569            self.props.base_color = self._state_color
     570
     571    def __members_changed_cb(self, adhoc_manager, channel, has_members):
     572        if channel == self._channel:
     573            if has_members == True:
     574                self._state_color = profile.get_color()
     575                self.props.base_color = self._state_color
     576                self._palette_icon.props.xo_color = self._state_color
     577            else:
     578                color = '%s,%s' % (profile.get_color().get_stroke_color(),
     579                                   style.COLOR_TRANSPARENT.get_svg())
     580                self._state_color = XoColor(color)
     581                self.props.base_color = self._state_color
     582                self._palette_icon.props.xo_color = self._state_color
     583
     584    def set_filter(self, query):
     585        name = self._NAME + str(self._channel)
     586        self._greyed_out = name.lower().find(query) == -1
     587        self._update_color()
     588
    446589
    447590class OlpcMeshView(CanvasPulsingIcon):
    448591    def __init__(self, mesh_mgr, channel):
    class MeshToolbar(gtk.Toolbar): 
    762905        return False
    763906
    764907
    765 class DeviceObserver(object):
    766     def __init__(self, box, device):
    767         self._box = box
     908class DeviceObserver(gobject.GObject):
     909    __gsignals__ = {
     910        'ap-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
     911                     ([gobject.TYPE_PYOBJECT])),
     912        'ap-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
     913                       ([gobject.TYPE_PYOBJECT]))
     914    }
     915    def __init__(self, device):
     916        gobject.GObject.__init__(self)
    768917        self._bus = dbus.SystemBus()
    769         self._device = device
     918        self.device = device
    770919
    771         wireless = dbus.Interface(self._device, _NM_WIRELESS_IFACE)
     920        wireless = dbus.Interface(device, _NM_WIRELESS_IFACE)
    772921        wireless.GetAccessPoints(reply_handler=self._get_access_points_reply_cb,
    773922                                 error_handler=self._get_access_points_error_cb)
    774923
    class DeviceObserver(object): 
    784933    def _get_access_points_reply_cb(self, access_points_o):
    785934        for ap_o in access_points_o:
    786935            ap = self._bus.get_object(_NM_SERVICE, ap_o)
    787             self._box.add_access_point(self._device, ap)
     936            self.emit('ap-added', ap)
    788937
    789938    def _get_access_points_error_cb(self, err):
    790939        logging.error('Failed to get access points: %s', err)
    791940
    792941    def __access_point_added_cb(self, access_point_o):
    793942        ap = self._bus.get_object(_NM_SERVICE, access_point_o)
    794         self._box.add_access_point(self._device, ap)
     943        self.emit('ap-added', ap)
    795944
    796945    def __access_point_removed_cb(self, access_point_o):
    797         self._box.remove_access_point(access_point_o)
     946        self.emit('ap-removed', access_point_o)
    798947
    799948    def disconnect(self):
    800949        self._bus.remove_signal_receiver(self.__access_point_added_cb,
    801950                                         signal_name='AccessPointAdded',
    802                                          path=self._device.object_path,
     951                                         path=self.device.object_path,
    803952                                         dbus_interface=_NM_WIRELESS_IFACE)
    804953        self._bus.remove_signal_receiver(self.__access_point_removed_cb,
    805954                                         signal_name='AccessPointRemoved',
    806                                          path=self._device.object_path,
     955                                         path=self.device.object_path,
    807956                                         dbus_interface=_NM_WIRELESS_IFACE)
    808957
    809958
    810959class NetworkManagerObserver(object):
     960
     961    _SHOW_ADHOC = '/desktop/sugar/network/adhoc'
     962
    811963    def __init__(self, box):
    812964        self._box = box
    813965        self._bus = None
    class NetworkManagerObserver(object): 
    815967        self._netmgr = None
    816968        self._olpc_mesh_device_o = None
    817969
     970        client = gconf.client_get_default()
     971        self._create_adhoc_networks = client.get_bool(self._SHOW_ADHOC)
     972
    818973    def listen(self):
    819974        try:
    820975            self._bus = dbus.SystemBus()
    class NetworkManagerObserver(object): 
    833988        self._bus.add_signal_receiver(self.__device_removed_cb,
    834989                                      signal_name='DeviceRemoved',
    835990                                      dbus_interface=_NM_IFACE)
     991        self._bus.add_signal_receiver(self.__properties_changed_cb,
     992                                      signal_name='PropertiesChanged',
     993                                      dbus_interface=_NM_IFACE)
    836994
    837995        settings = network.get_settings()
    838996        if settings is not None:
    class NetworkManagerObserver(object): 
    8761034
    8771035        device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
    8781036        if device_type == network.DEVICE_TYPE_802_11_WIRELESS:
    879             self._devices[device_o] = DeviceObserver(self._box, device)
     1037            self._devices[device_o] = DeviceObserver(device)
     1038            self._devices[device_o].connect('ap_added', self.__ap_added_cb)
     1039            self._devices[device_o].connect('ap_removed', self.__ap_removed_cb)
     1040            if self._create_adhoc_networks:
     1041                self._box.add_adhoc_networks(device)
    8801042        elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
    8811043            self._olpc_mesh_device_o = device_o
    8821044            self._box.enable_olpc_mesh(device)
    class NetworkManagerObserver(object): 
    8921054            observer = self._devices[device_o]
    8931055            observer.disconnect()
    8941056            del self._devices[device_o]
     1057            if self._create_adhoc_networks:
     1058                self._box.remove_adhoc_networks()
    8951059            return
    8961060
    8971061        if self._olpc_mesh_device_o == device_o:
    8981062            self._box.disable_olpc_mesh(device_o)
    8991063
     1064    def __ap_added_cb(self, device_observer, access_point):
     1065        self._box.add_access_point(device_observer.device, access_point)
     1066
     1067    def __ap_removed_cb(self, device_observer, access_point_o):
     1068        self._box.remove_access_point(access_point_o)
     1069
     1070    def __properties_changed_cb(self, properties):
     1071        if 'WirelessHardwareEnabled' in properties:
     1072            if properties['WirelessHardwareEnabled'] == False:
     1073                if not self._create_adhoc_networks:
     1074                    self._box.remove_adhoc_networks()
     1075            if properties['WirelessHardwareEnabled'] == True:
     1076                for device in self._devices:
     1077                    if self._create_adhoc_networks:
     1078                        self._box.add_adhoc_networks(device)
     1079
     1080
    9001081class MeshBox(gtk.VBox):
    9011082    __gtype_name__ = 'SugarMeshBox'
    9021083
    class MeshBox(gtk.VBox): 
    9061087        gobject.GObject.__init__(self)
    9071088
    9081089        self.wireless_networks = {}
     1090        self._adhoc_manager = None
     1091        self._adhoc_networks = []
    9091092
    9101093        self._model = neighborhood.get_model()
    9111094        self._buddies = {}
    class MeshBox(gtk.VBox): 
    10631246            ap.disconnect()
    10641247            return
    10651248
     1249        if self._adhoc_manager is not None and \
     1250                network.is_sugar_adhoc_network(ap.name) and \
     1251                ap.mode == network.NM_802_11_MODE_ADHOC:
     1252
     1253            if old_hash is None: # new Ad-hoc network finished initializing
     1254                self._adhoc_manager.add_access_point(ap)
     1255            # we are called as well in other cases but we do not need to
     1256            # act here as we don't display signal strength for Ad-hoc networks
     1257            return
     1258
    10661259        if old_hash is None: # new AP finished initializing
    10671260            self._add_ap_to_network(ap)
    10681261            return
    class MeshBox(gtk.VBox): 
    10851278        ap.initialize()
    10861279
    10871280    def remove_access_point(self, ap_o):
     1281        if self._adhoc_manager is not None:
     1282            if self._adhoc_manager.remove_access_point(ap_o) == True:
     1283                return
     1284
    10881285        # we don't keep an index of ap object path to network, but since
    10891286        # we'll only ever have a handful of networks, just try them all...
    10901287        for net in self.wireless_networks.values():
    class MeshBox(gtk.VBox): 
    11011298        # it (e.g. olpc-mesh adhoc network)
    11021299        logging.debug('Can not remove access point %s' % ap_o)
    11031300
     1301    def add_adhoc_networks(self, device):
     1302        if self._adhoc_manager is None:
     1303            self._adhoc_manager = adhoc.get_manager()
     1304            self._adhoc_manager.listen(device)
     1305        self._add_adhoc_network_icon(1)
     1306        self._add_adhoc_network_icon(6)
     1307        self._add_adhoc_network_icon(11)
     1308        self._adhoc_manager.autoconnect()
     1309
     1310    def remove_adhoc_networks(self):
     1311        for icon in self._adhoc_networks:
     1312            icon.disconnect()
     1313            self._layout.remove(icon)
     1314        self._adhoc_networks = []
     1315
     1316    def _add_adhoc_network_icon(self, channel):
     1317        icon = SugarAdhocView(channel)
     1318        self._layout.add(icon)
     1319        self._adhoc_networks.append(icon)
     1320
    11041321    def _add_olpc_mesh_icon(self, mesh_mgr, channel):
    11051322        icon = OlpcMeshView(mesh_mgr, channel)
    11061323        self._layout.add(icon)
  • src/jarabe/model/Makefile.am

    diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am
    index e9f0700..4650c3b 100644
    a b  
    11sugardir = $(pythondir)/jarabe/model
    22sugar_PYTHON =                  \
     3        adhoc.py                \
    34        __init__.py             \
    45        buddy.py                \
    56        bundleregistry.py       \
  • new file src/jarabe/model/adhoc.py

    diff --git a/src/jarabe/model/adhoc.py b/src/jarabe/model/adhoc.py
    new file mode 100644
    index 0000000..ca7c79f
    - +  
     1# Copyright (C) 2010 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 sugar.util import unique_id
     25from jarabe.model.network import IP4Config
     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_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless'
     32_NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
     33_NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
     34
     35_adhoc_manager = None
     36
     37
     38def get_manager():
     39    global _adhoc_manager
     40
     41    if not _adhoc_manager:
     42        _adhoc_manager = AdHocManager()
     43    return _adhoc_manager
     44
     45
     46class AdHocManager(gobject.GObject):
     47    """To mimic the mesh behavior on devices where mesh hardware is
     48    not available we support the creation of an Ad-hoc network on
     49    three channels 1, 6, 11. If Sugar sees no "known" network when it
     50    starts, it does autoconnect to an Ad-hoc network.
     51
     52    """
     53
     54    __gsignals__ = {
     55        'members-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
     56                            ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
     57        'state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
     58                            ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]))
     59    }
     60
     61    _AUTOCONNECT_TIMEOUT = 30
     62    _CHANNEL_1 = 1
     63    _CHANNEL_6 = 6
     64    _CHANNEL_11 = 11
     65
     66    def __init__(self):
     67        gobject.GObject.__init__(self)
     68
     69        self._bus = dbus.SystemBus()
     70        self._device = None
     71        self._idle_source = 0
     72        self._device_state = network.DEVICE_STATE_UNKNOWN
     73
     74        self._current_channel = None
     75        self._networks = {self._CHANNEL_1: None,
     76                          self._CHANNEL_6: None,
     77                          self._CHANNEL_11: None}
     78
     79    def listen(self, device):
     80        self._device = device
     81        props = dbus.Interface(device,
     82                                   'org.freedesktop.DBus.Properties')
     83        props.Get(_NM_DEVICE_IFACE, 'State',
     84                  reply_handler=self.__get_device_state_reply_cb,
     85                  error_handler=self.__get_state_error_cb)
     86
     87        self._bus.add_signal_receiver(self.__device_state_changed_cb,
     88                                      signal_name='StateChanged',
     89                                      path=self._device.object_path,
     90                                      dbus_interface=_NM_DEVICE_IFACE)
     91
     92        self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
     93                                      signal_name='PropertiesChanged',
     94                                      path=self._device.object_path,
     95                                      dbus_interface=_NM_WIRELESS_IFACE)
     96
     97    def _have_configured_connections(self):
     98        return len(network.get_settings().connections) > 0
     99
     100    def autoconnect(self):
     101        """Autoconnect to an Ad-hoc network"""
     102        if self._have_configured_connections():
     103            self._autoconnect_adhoc_timer()
     104        else:
     105            self._autoconnect_adhoc()
     106
     107    def __get_state_error_cb(self, err):
     108        logging.debug('Error getting the device state: %s', err)
     109
     110    def __get_device_state_reply_cb(self, state):
     111        self._device_state = state
     112
     113    def __device_state_changed_cb(self, new_state, old_state, reason):
     114        self._device_state = new_state
     115        self._update_state()
     116
     117    def __wireless_properties_changed_cb(self, properties):
     118        if 'ActiveAccessPoint' in properties and \
     119                properties['ActiveAccessPoint'] != '/':
     120            active_ap = self._bus.get_object(_NM_SERVICE,
     121                                             properties['ActiveAccessPoint'])
     122            props = dbus.Interface(active_ap, dbus.PROPERTIES_IFACE)
     123            props.GetAll(_NM_ACCESSPOINT_IFACE, byte_arrays=True,
     124                         reply_handler=self.__get_all_ap_props_reply_cb,
     125                         error_handler=self.__get_all_ap_props_error_cb)
     126
     127    def __get_all_ap_props_reply_cb(self, properties):
     128        if properties['Mode'] == network.NM_802_11_MODE_ADHOC and \
     129                'Frequency' in properties:
     130            frequency = properties['Frequency']
     131            try:
     132                self._current_channel = network.frequency_to_channel(frequency)
     133            except KeyError:
     134                logging.debug("Error getting the Frequency.")
     135        else:
     136            self._current_channel = None
     137        self._update_state()
     138
     139    def __get_all_ap_props_error_cb(self, err):
     140        logging.error('Error getting the access point properties: %s', err)
     141
     142    def _update_state(self):
     143        self.emit('state-changed', self._current_channel, self._device_state)
     144
     145    def _autoconnect_adhoc_timer(self):
     146        """Start a timer which basically looks for 30 seconds of inactivity
     147        on the device, then does autoconnect to an Ad-hoc network.
     148
     149        """
     150        if self._idle_source != 0:
     151            gobject.source_remove(self._idle_source)
     152        self._idle_source = gobject.timeout_add_seconds( \
     153                self._AUTOCONNECT_TIMEOUT, self.__idle_check_cb)
     154
     155    def _autoconnect_adhoc(self):
     156        """First we try if there is an Ad-hoc network that is used by other
     157        learners in the area, if not we default to channel 1.
     158
     159        """
     160        if self._networks[self._CHANNEL_1] is not None:
     161            self._connect(self._CHANNEL_1)
     162        elif self._networks[self._CHANNEL_6] is not None:
     163            self._connect(self._CHANNEL_6)
     164        elif self._networks[self._CHANNEL_11] is not None:
     165            self._connect(self._CHANNEL_11)
     166        else:
     167            self._connect(self._CHANNEL_1)
     168
     169    def __idle_check_cb(self):
     170        if  self._device_state == network.DEVICE_STATE_DISCONNECTED:
     171            logging.debug("Connect to Ad-hoc network due to inactivity.")
     172            self._autoconnect_adhoc()
     173        return False
     174
     175    def activate_channel(self, channel):
     176        self._connect(channel)
     177
     178    def _connect(self, channel):
     179        name = "Ad-hoc Network %d" % channel
     180        connection = network.find_connection_by_ssid(name)
     181        if connection is None:
     182            settings = Settings()
     183            settings.connection.id = name
     184            settings.connection.uuid = unique_id()
     185            settings.connection.type = '802-11-wireless'
     186            settings.wireless.ssid = dbus.ByteArray(name)
     187            settings.wireless.band = 'bg'
     188            settings.wireless.channel = channel
     189            settings.wireless.mode = 'adhoc'
     190            settings.ip4_config = IP4Config()
     191            settings.ip4_config.method = 'link-local'
     192
     193            connection = network.add_connection(name, settings)
     194
     195        obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
     196        netmgr = dbus.Interface(obj, _NM_IFACE)
     197
     198        netmgr.ActivateConnection(network.SETTINGS_SERVICE,
     199                                  connection.path,
     200                                  self._device.object_path,
     201                                  '/',
     202                                  reply_handler=self.__activate_reply_cb,
     203                                  error_handler=self.__activate_error_cb)
     204
     205    def deactivate_active_channel(self):
     206        obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
     207        netmgr = dbus.Interface(obj, _NM_IFACE)
     208
     209        netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE)
     210        netmgr_props.Get(_NM_IFACE, 'ActiveConnections', \
     211                reply_handler=self.__get_active_connections_reply_cb,
     212                error_handler=self.__get_active_connections_error_cb)
     213
     214    def __get_active_connections_reply_cb(self, active_connections_o):
     215        for connection_o in active_connections_o:
     216            obj = self._bus.get_object(_NM_IFACE, connection_o)
     217            props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
     218            state = props.Get(_NM_ACTIVE_CONN_IFACE, 'State')
     219            if state == network.NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
     220                access_point_o = props.Get(_NM_ACTIVE_CONN_IFACE,
     221                                           'SpecificObject')
     222                if access_point_o != '/':
     223                    obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
     224                    netmgr = dbus.Interface(obj, _NM_IFACE)
     225                    netmgr.DeactivateConnection(connection_o)
     226
     227    def __get_active_connections_error_cb(self, err):
     228        logging.error('Error getting the active connections: %s', err)
     229
     230    def __activate_reply_cb(self, connection):
     231        logging.debug('Ad-hoc network created: %s', connection)
     232
     233    def __activate_error_cb(self, err):
     234        logging.error('Failed to create Ad-hoc network: %s', err)
     235
     236    def add_access_point(self, access_point):
     237        """If a new adhoc network is announcd that is named like an Ad-hoc
     238        network we take this as an indication that other learners are
     239        using the network an indicate this.
     240
     241        """
     242        if ' 1' == access_point.name[-2:]:
     243            self.emit('members-changed', self._CHANNEL_1, True)
     244            self._networks[self._CHANNEL_1] = access_point
     245        elif '6' == access_point.name[-1]:
     246            self.emit('members-changed', self._CHANNEL_6, True)
     247            self._networks[self._CHANNEL_6] = access_point
     248        elif '11' == access_point.name[-2:]:
     249            self.emit('members-changed', self._CHANNEL_11, True)
     250            self._networks[self._CHANNEL_11] = access_point
     251
     252    def remove_access_point(self, ap_object_path):
     253        """When the last user of the network goes away we remove
     254        the access point and indicate this.
     255
     256        """
     257        for channel in self._networks:
     258            if self._networks[channel] is not None:
     259                if self._networks[channel].model.object_path == ap_object_path:
     260                    self.emit('members-changed', channel, False)
     261                    self._networks[channel] = None
     262                    return True
     263        return False
     264
     265    def disconnect(self):
     266        self._bus.remove_signal_receiver(self.__device_state_changed_cb,
     267                                         signal_name='StateChanged',
     268                                         path=self._device.object_path,
     269                                         dbus_interface=_NM_DEVICE_IFACE)
     270        self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
     271                                         signal_name='PropertiesChanged',
     272                                         path=self._device.object_path,
     273                                         dbus_interface=_NM_WIRELESS_IFACE)
  • src/jarabe/model/network.py

    diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py
    index 47db43f..a51dc4e 100644
    a b  
    11# Copyright (C) 2008 Red Hat, Inc.
    22# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
    3 # Copyright (C) 2009 One Laptop per Child
     3# Copyright (C) 2009-2010 One Laptop per Child
    44# Copyright (C) 2009 Paraguay Educa, Martin Abente
    55# Copyright (C) 2010 Plan Ceibal, Daniel Castelo
    66#
    GSM_PUK_PATH = '/desktop/sugar/network/gsm/puk' 
    100100_nm_settings = None
    101101_conn_counter = 0
    102102
     103
     104def frequency_to_channel(frequency):
     105    ftoc = {2412: 1, 2417: 2, 2422: 3, 2427: 4,
     106            2432: 5, 2437: 6, 2442: 7, 2447: 8,
     107            2452: 9, 2457: 10, 2462: 11, 2467: 12,
     108            2472: 13}
     109    return ftoc[frequency]
     110
     111def is_sugar_adhoc_network(ssid):
     112    return ssid.startswith('Ad-hoc Network')
     113
     114
    103115class WirelessSecurity(object):
    104116    def __init__(self):
    105117        self.key_mgmt = None
    class Wireless(object): 
    127139        self.security = None
    128140        self.mode = None
    129141        self.band = None
     142        self.channel = None
    130143
    131144    def get_dict(self):
    132145        wireless = {'ssid': self.ssid}
    class Wireless(object): 
    136149            wireless['mode'] = self.mode
    137150        if self.band:
    138151            wireless['band'] = self.band
     152        if self.channel:
     153            wireless['channel'] = self.channel
    139154        return wireless
    140155
    141156
    class AccessPoint(gobject.GObject): 
    476491        self.wpa_flags = 0
    477492        self.rsn_flags = 0
    478493        self.mode = 0
     494        self.channel = 0
    479495
    480496    def initialize(self):
    481497        model_props = dbus.Interface(self.model,
    class AccessPoint(gobject.GObject): 
    545561            self.rsn_flags = properties['RsnFlags']
    546562        if 'Mode' in properties:
    547563            self.mode = properties['Mode']
     564        if 'Frequency' in properties:
     565            try:
     566                self.channel = frequency_to_channel(properties['Frequency'])
     567            except KeyError:
     568                self.channel = 0
     569
    548570        self._initialized = True
    549571        self.emit('props-changed', old_hash)
    550572