Ticket #330: 0001-Group-access-points-by-network-330.patch
File 0001-Group-access-points-by-network-330.patch, 20.7 KB (added by dsd, 14 years ago) |
---|
-
src/jarabe/desktop/meshbox.py
From 3f260708394122c5a358743aec54a5f879d6237d Mon Sep 17 00:00:00 2001 From: Daniel Drake <dsd@laptop.org> Date: Fri, 13 Nov 2009 10:31:12 +0000 Subject: [PATCH] Group access points by network (#330) This patch implements the same AP-grouping logic as GNOME's nm-applet. APs that are from the same network are now shown as just a single network icon on the network view. If connected to the network, the circle displays the signal strength of the AP that you're connected to. If you aren't connected, it displays the signal strength of the strongest AP in that network. Showing all the APs is redundant anyway, since sugar doesn't really have any say in which AP is connected to after the user selects the network. Fixes a 0.84 regression where networks were split. Restores 0.82 behaviour of one circle per network. --- src/jarabe/desktop/meshbox.py | 367 +++++++++++++++++++++++++++++------------ 1 files changed, 260 insertions(+), 107 deletions(-) diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py index f8c446d..87ce7c6 100644
a b _NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active' 60 60 61 61 _ICON_NAME = 'network-wireless' 62 62 63 class AccessPointView(CanvasPulsingIcon): 63 class AccessPoint(gobject.GObject): 64 __gsignals__ = { 65 'props-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, 66 ([gobject.TYPE_PYOBJECT])) 67 } 68 64 69 def __init__(self, device, model): 70 self.__gobject_init__() 71 self.device = device 72 self.model = model 73 74 self._initialized = False 75 self._bus = dbus.SystemBus() 76 77 self.name = '' 78 self.strength = 0 79 self.flags = 0 80 self.wpa_flags = 0 81 self.rsn_flags = 0 82 self.mode = 0 83 84 def initialize(self): 85 model_props = dbus.Interface(self.model, 86 'org.freedesktop.DBus.Properties') 87 model_props.GetAll(_NM_ACCESSPOINT_IFACE, byte_arrays=True, 88 reply_handler=self._ap_properties_changed_cb, 89 error_handler=self._get_all_props_error_cb) 90 91 self._bus.add_signal_receiver(self._ap_properties_changed_cb, 92 signal_name='PropertiesChanged', 93 path=self.model.object_path, 94 dbus_interface=_NM_ACCESSPOINT_IFACE, 95 byte_arrays=True) 96 97 # this is a hash which uniquely identifies the network that this AP 98 # is a bridge to. i.e. its expected for 2 APs with identical SSID and 99 # other settings to have the same network hash, because we assume that 100 # they are a part of the same underlying network. 101 def network_hash(self): 102 # based on logic from nm-applet 103 fl = 0 104 105 if self.mode == network.NM_802_11_MODE_INFRA: 106 fl |= 1 << 0 107 elif self.mode == network.NM_802_11_MODE_ADHOC: 108 fl |= 1 << 1 109 else: 110 fl |= 1 << 2 111 112 # Separate out no encryption, WEP-only, and WPA-capable */ 113 if (not (self.flags & network.NM_802_11_AP_FLAGS_PRIVACY)) \ 114 and self.wpa_flags == network.NM_802_11_AP_SEC_NONE \ 115 and self.rsn_flags == network.NM_802_11_AP_SEC_NONE: 116 fl |= 1 << 3 117 elif (self.flags & network.NM_802_11_AP_FLAGS_PRIVACY) \ 118 and self.wpa_flags == network.NM_802_11_AP_SEC_NONE \ 119 and self.rsn_flags == network.NM_802_11_AP_SEC_NONE: 120 fl |= 1 << 4 121 elif (not (self.flags & network.NM_802_11_AP_FLAGS_PRIVACY)) \ 122 and self.wpa_flags != network.NM_802_11_AP_SEC_NONE \ 123 and self.rsn_flags != networkNM_802_11_AP_SEC_NONE: 124 fl |= 1 << 5 125 else: 126 fl |= 1 << 6 127 128 hashstr = str(fl) + "@" + self.name 129 return hash(hashstr) 130 131 def _update_properties(self, properties): 132 if self._initialized: 133 old_hash = self.network_hash() 134 else: 135 old_hash = None 136 137 if 'Ssid' in properties: 138 self.name = properties['Ssid'] 139 if 'Strength' in properties: 140 self.strength = properties['Strength'] 141 if 'Flags' in properties: 142 self.flags = properties['Flags'] 143 if 'WpaFlags' in properties: 144 self.wpa_flags = properties['WpaFlags'] 145 if 'RsnFlags' in properties: 146 self.rsn_flags = properties['RsnFlags'] 147 if 'Mode' in properties: 148 self.mode = properties['Mode'] 149 self._initialized = True 150 self.emit('props-changed', old_hash) 151 152 def _get_all_props_error_cb(self, err): 153 logging.debug('Error getting the access point properties: %s', err) 154 155 def _ap_properties_changed_cb(self, properties): 156 self._update_properties(properties) 157 158 def disconnect(self): 159 self._bus.remove_signal_receiver(self._ap_properties_changed_cb, 160 signal_name='PropertiesChanged', 161 path=self.model.object_path, 162 dbus_interface=_NM_ACCESSPOINT_IFACE) 163 164 class WirelessNetworkView(CanvasPulsingIcon): 165 def __init__(self, initial_ap): 65 166 CanvasPulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE, 66 167 cache=True) 67 168 self._bus = dbus.SystemBus() 68 self._device = device 69 self._model = model 169 self._access_points = {initial_ap.model.object_path: initial_ap} 170 self._active_ap = None 171 self._device = initial_ap.device 70 172 self._palette_icon = None 71 173 self._disconnect_item = None 72 174 self._connect_item = None 73 175 self._greyed_out = False 74 self._name = ''75 self._ strength = 076 self._ flags = 077 self._ wpa_flags = 078 self._ rsn_flags = 079 self._ mode = network.NM_802_11_MODE_UNKNOWN176 self._name = initial_ap.name 177 self._mode = initial_ap.mode 178 self._strength = initial_ap.strength 179 self._flags = initial_ap.flags 180 self._wpa_flags = initial_ap.wpa_flags 181 self._rsn_flags = initial_ap.rsn_flags 80 182 self._device_caps = 0 81 183 self._device_state = None 82 184 self._connection = None 83 self._active = True 84 self._color = None 185 186 if self._mode == network.NM_802_11_MODE_ADHOC: 187 encoded_color = self._name.split("#", 1) 188 if len(encoded_color) == 2: 189 self._color = xocolor.XoColor('#' + encoded_color[1]) 190 if self._mode == network.NM_802_11_MODE_INFRA: 191 sha_hash = hashlib.sha1() 192 data = self._name + hex(self._flags) 193 sha_hash.update(data) 194 digest = hash(sha_hash.digest()) 195 index = digest % len(xocolor.colors) 196 197 self._color = xocolor.XoColor('%s,%s' % 198 (xocolor.colors[index][0], 199 xocolor.colors[index][1])) 85 200 86 201 self.connect('button-release-event', self.__button_release_event_cb) 87 202 … … class AccessPointView(CanvasPulsingIcon): 91 206 92 207 self._palette = self._create_palette() 93 208 self.set_palette(self._palette) 209 self._palette_icon.props.xo_color = self._color 94 210 95 model_props = dbus.Interface(model, 'org.freedesktop.DBus.Properties') 96 model_props.GetAll(_NM_ACCESSPOINT_IFACE, byte_arrays=True, 97 reply_handler=self.__get_all_props_reply_cb, 98 error_handler=self.__get_all_props_error_cb) 99 100 self._bus.add_signal_receiver(self.__ap_properties_changed_cb, 101 signal_name='PropertiesChanged', 102 path=model.object_path, 103 dbus_interface=_NM_ACCESSPOINT_IFACE, 104 byte_arrays=True) 211 if network.find_connection(self._name) is not None: 212 self.props.badge_name = "emblem-favorite" 213 self._palette_icon.props.badge_name = "emblem-favorite" 214 elif initial_ap.flags == network.NM_802_11_AP_FLAGS_PRIVACY: 215 self.props.badge_name = "emblem-locked" 216 self._palette_icon.props.badge_name = "emblem-locked" 217 else: 218 self.props.badge_name = None 219 self._palette_icon.props.badge_name = None 105 220 106 221 interface_props = dbus.Interface(self._device, 107 222 'org.freedesktop.DBus.Properties') … … class AccessPointView(CanvasPulsingIcon): 117 232 118 233 self._bus.add_signal_receiver(self.__device_state_changed_cb, 119 234 signal_name='StateChanged', 120 path= device.object_path,235 path=self._device.object_path, 121 236 dbus_interface=_NM_DEVICE_IFACE) 122 237 self._bus.add_signal_receiver(self.__wireless_properties_changed_cb, 123 238 signal_name='PropertiesChanged', 124 path= device.object_path,239 path=self._device.object_path, 125 240 dbus_interface=_NM_WIRELESS_IFACE) 126 241 127 242 def _create_palette(self): … … class AccessPointView(CanvasPulsingIcon): 148 263 self._device_state = new_state 149 264 self._update_state() 150 265 151 def __ap_properties_changed_cb(self, properties): 152 self._update_properties(properties) 266 def __update_active_ap(self, ap_path): 267 if ap_path in self._access_points: 268 # save reference to active AP, so that we always display the 269 # strength of that one 270 self._active_ap = self._access_points[ap_path] 271 self.update_strength() 272 self._update_state() 273 elif self._active_ap is not None: 274 # revert to showing state of strongest AP again 275 self._active_ap = None 276 self.update_strength() 277 self._update_state() 153 278 154 279 def __wireless_properties_changed_cb(self, properties): 155 280 if 'ActiveAccessPoint' in properties: 156 ap = properties['ActiveAccessPoint'] 157 self._active = (ap == self._model.object_path) 158 self._update_state() 159 160 def _update_properties(self, properties): 161 if 'Mode' in properties: 162 self._mode = properties['Mode'] 163 self._color = None 164 if 'Ssid' in properties: 165 self._name = properties['Ssid'] 166 self._color = None 167 if 'Strength' in properties: 168 self._strength = properties['Strength'] 169 if 'Flags' in properties: 170 self._flags = properties['Flags'] 171 if 'WpaFlags' in properties: 172 self._wpa_flags = properties['WpaFlags'] 173 if 'RsnFlags' in properties: 174 self._rsn_flags = properties['RsnFlags'] 281 self.__update_active_ap(properties['ActiveAccessPoint']) 175 282 176 if self._color == None: 177 if self._mode == network.NM_802_11_MODE_ADHOC: 178 encoded_color = self._name.split("#", 1) 179 if len(encoded_color) == 2: 180 self._color = XoColor('#' + encoded_color[1]) 181 if self._mode == network.NM_802_11_MODE_INFRA: 182 sha_hash = hashlib.sha1() 183 data = self._name + hex(self._flags) 184 sha_hash.update(data) 185 digest = hash(sha_hash.digest()) 186 index = digest % len(xocolor.colors) 187 188 self._color = XoColor('%s,%s' % 189 (xocolor.colors[index][0], 190 xocolor.colors[index][1])) 191 self._update() 192 193 def __get_active_ap_reply_cb(self, ap): 194 self._active = (ap == self._model.object_path) 195 self._update_state() 283 def __get_active_ap_reply_cb(self, ap_path): 284 self.__update_active_ap(ap_path) 196 285 197 286 def __get_active_ap_error_cb(self, err): 198 287 logging.debug('Error getting the active access point: %s', err) … … class AccessPointView(CanvasPulsingIcon): 217 306 logging.debug('Error getting the access point properties: %s', err) 218 307 219 308 def _update(self): 220 if network.find_connection(self._name) is not None:221 self.props.badge_name = "emblem-favorite"222 self._palette_icon.props.badge_name = "emblem-favorite"223 elif self._flags == network.NM_802_11_AP_FLAGS_PRIVACY:224 self.props.badge_name = "emblem-locked"225 self._palette_icon.props.badge_name = "emblem-locked"226 else:227 self.props.badge_name = None228 self._palette_icon.props.badge_name = None229 230 self._palette.props.primary_text = self._name231 232 309 self._update_state() 233 310 self._update_color() 234 311 235 312 def _update_state(self): 236 if self._active :313 if self._active_ap is not None: 237 314 state = self._device_state 238 315 else: 239 316 state = network.DEVICE_STATE_UNKNOWN … … class AccessPointView(CanvasPulsingIcon): 283 360 else: 284 361 self.props.base_color = self._color 285 362 286 self._palette_icon.props.xo_color = self._color287 288 363 def _disconnect_activate_cb(self, item): 289 364 pass 290 365 291 292 366 def _add_ciphers_from_flags(self, flags, pairwise): 293 367 ciphers = [] 294 368 if pairwise: … … class AccessPointView(CanvasPulsingIcon): 386 460 obj = self._bus.get_object(_NM_SERVICE, _NM_PATH) 387 461 netmgr = dbus.Interface(obj, _NM_IFACE) 388 462 463 # Choose the AP with the strongest signal 464 # This is not the deciding factor for which AP ends up being connected 465 # to (you can even omit this parameter), but just-in-case this has 466 # relevance in future... 467 best = None 468 for ap in self._access_points.values(): 469 if best is None or ap.strength > best.strength: 470 best = ap 471 389 472 netmgr.ActivateConnection(network.SETTINGS_SERVICE, connection.path, 390 473 self._device.object_path, 391 self._model.object_path,474 best.model.object_path, 392 475 reply_handler=self.__activate_reply_cb, 393 476 error_handler=self.__activate_error_cb) 394 477 … … class AccessPointView(CanvasPulsingIcon): 407 490 keydialog.create(self._name, self._flags, self._wpa_flags, 408 491 self._rsn_flags, self._device_caps, response) 409 492 410 def disconnect(self): 411 self._bus.remove_signal_receiver(self.__ap_properties_changed_cb, 412 signal_name='PropertiesChanged', 413 path=self._model.object_path, 414 dbus_interface=_NM_ACCESSPOINT_IFACE) 493 def update_strength(self): 494 if self._active_ap is not None: 495 # display strength of AP that we are connected to 496 new_strength = self._active_ap.strength 497 else: 498 # display the strength of the strongest AP that makes up this 499 # network 500 new_strength = 0 501 for ap in self._access_points.values(): 502 if ap.strength > new_strength: 503 new_strength = ap.strength 504 505 if new_strength != self._strength: 506 self._strength = new_strength 507 self._update_state() 415 508 509 def add_ap(self, ap): 510 self._access_points[ap.model.object_path] = ap 511 self.update_strength() 512 513 def remove_ap(self, ap): 514 path = ap.model.object_path 515 if path not in self._access_points: 516 return 517 del self._access_points[path] 518 self.update_strength() 519 520 def num_aps(self): 521 return len(self._access_points) 522 523 def find_ap(self, ap_path): 524 if ap_path not in self._access_points: 525 return None 526 return self._access_points[ap_path] 527 528 def disconnect(self): 416 529 self._bus.remove_signal_receiver(self.__device_state_changed_cb, 417 530 signal_name='StateChanged', 418 531 path=self._device.object_path, … … class NetworkManagerObserver(object): 693 806 state = props.Get(_NM_ACTIVE_CONN_IFACE, 'State') 694 807 if state == network.NM_ACTIVE_CONNECTION_STATE_ACTIVATING: 695 808 ap_o = props.Get(_NM_ACTIVE_CONN_IFACE, 'SpecificObject') 809 found = False 696 810 if ap_o != '/': 697 ap_view = self._box.access_points[ap_o] 698 ap_view.create_keydialog(kwargs['response']) 699 else: 811 for net in self._box.wireless_networks.values(): 812 if net.find_ap(ap_o) is not None: 813 found = True 814 net.create_keydialog(kwargs['response']) 815 if not found: 700 816 logging.error('Could not determine AP for' 701 817 ' specific object %s' % conn_o) 702 818 … … class MeshBox(gtk.VBox): 735 851 736 852 gobject.GObject.__init__(self) 737 853 738 self. access_points = {}854 self.wireless_networks = {} 739 855 740 856 self._model = neighborhood.get_model() 741 857 self._buddies = {} … … class MeshBox(gtk.VBox): 863 979 del self._activities[activity_model.get_id()] 864 980 icon.destroy() 865 981 866 def add_access_point(self, device, ap): 867 icon = AccessPointView(device, ap) 868 self._layout.add(icon) 982 # add AP to its corresponding network icon on the desktop, 983 # creating one if it doesn't already exist 984 def _add_ap_to_network(self, ap): 985 hash = ap.network_hash() 986 if hash in self.wireless_networks: 987 self.wireless_networks[hash].add_ap(ap) 988 else: 989 # this is a new network 990 icon = WirelessNetworkView(ap) 991 self.wireless_networks[hash] = icon 992 self._layout.add(icon) 993 if hasattr(icon, 'set_filter'): 994 icon.set_filter(self._query) 869 995 870 if hasattr(icon, 'set_filter'): 871 icon.set_filter(self._query) 996 def _ap_props_changed_cb(self, ap, old_hash): 997 if old_hash is None: # new AP finished initializing 998 self._add_ap_to_network(ap) 999 return 872 1000 873 self.access_points[ap.object_path] = icon 1001 hash = ap.network_hash() 1002 if old_hash == hash: 1003 # no change in network identity, so just update signal strengths 1004 self.wireless_networks[hash].update_strength() 1005 return 1006 1007 # properties change includes a change of the identity of the network 1008 # that it is on. so create this as a new network. 1009 self.wireless_networks[old_hash].remove_ap(ap) 1010 self._add_ap_to_network(ap) 1011 1012 def add_access_point(self, device, ap_o): 1013 ap = AccessPoint(device, ap_o) 1014 ap.connect('props-changed', self._ap_props_changed_cb) 1015 ap.initialize() 874 1016 875 1017 def remove_access_point(self, ap_o): 876 if ap_o in self.access_points: 877 icon = self.access_points[ap_o] 878 icon.disconnect() 879 self._layout.remove(icon) 880 del self.access_points[ap_o] 881 else: 882 logging.error('Can not remove access point %s', ap_o) 1018 # we don't keep an index of ap object path to network, but since 1019 # we'll only ever have a handful of networks, just try them all... 1020 for net in self.wireless_networks.values(): 1021 ap = net.find_ap(ap_o) 1022 if not ap: 1023 continue 1024 1025 ap.disconnect() 1026 net.remove_ap(ap) 1027 1028 # if that was the last AP in the network, then remove the network 1029 if net.num_aps() == 0: 1030 net.disconnect() 1031 self._layout.remove(net) 1032 del self.wireless_networks[ap.network_hash()] 1033 return 1034 1035 logging.error('Can not remove access point %s', ap_o) 883 1036 884 1037 def suspend(self): 885 1038 if not self._suspended: 886 1039 self._suspended = True 887 for ap in self.access_points.values():888 ap.props.paused = True1040 for net in self.wireless_networks.values(): 1041 net.props.paused = True 889 1042 890 1043 def resume(self): 891 1044 if self._suspended: 892 1045 self._suspended = False 893 for ap in self.access_points.values():894 ap.props.paused = False1046 for net in self.wireless_networks.values(): 1047 net.props.paused = False 895 1048 896 1049 def _toolbar_query_changed_cb(self, toolbar, query): 897 1050 self._query = query.lower()