From bcebef6fb02f20f1ac456cbc43c7ab3062f2d962 Mon Sep 17 00:00:00 2001 From: Simon Schampijer Date: Tue, 24 Aug 2010 17:49:31 +0000 Subject: GSM: show connection errors #1759 (Daniel Castelo) --- diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py index 6171f39..0789f9c 100644 --- a/extensions/deviceicon/network.py +++ b/extensions/deviceicon/network.py @@ -66,7 +66,7 @@ _GSM_STATE_NOT_READY = 0 _GSM_STATE_DISCONNECTED = 1 _GSM_STATE_CONNECTING = 2 _GSM_STATE_CONNECTED = 3 -_GSM_STATE_NEED_AUTH = 4 +_GSM_STATE_FAILED = 4 class WirelessPalette(Palette): @@ -214,34 +214,53 @@ class GsmPalette(Palette): Palette.__init__(self, label=_('Wireless modem')) self._current_state = None + self._failed_connection = False - self._toggle_state_item = gtk.MenuItem('') + self._toggle_state_item = MenuItem('') self._toggle_state_item.connect('activate', self.__toggle_state_cb) self.menu.append(self._toggle_state_item) self._toggle_state_item.show() - self.set_state(_GSM_STATE_NOT_READY) - self.info_box = gtk.VBox() - self.data_label = gtk.Label() - self.data_label.props.xalign = 0.0 - label_alignment = self._add_widget_with_padding(self.data_label) - self.info_box.pack_start(label_alignment) - self.data_label.show() + self.error_title_label = gtk.Label("") + self.error_title_label.set_alignment(0, 0.5) + self.error_title_label.set_line_wrap(True) + self.info_box.pack_start(self.error_title_label) + self.error_description_label = gtk.Label("") + self.error_description_label.set_alignment(0, 0.5) + self.error_description_label.set_line_wrap(True) + self.info_box.pack_start(self.error_description_label) + + self.connection_info_box = gtk.HBox() + icon = Icon(icon_name='data-upload', icon_size=gtk.ICON_SIZE_MENU) + self.connection_info_box.pack_start(icon) + icon.show() + + self._data_label_up = gtk.Label() + self._data_label_up.props.xalign = 0.0 + label_alignment = self._add_widget_with_padding(self._data_label_up) + self.connection_info_box.pack_start(label_alignment) + self._data_label_up.show() label_alignment.show() - self.connection_time_label = gtk.Label() - self.connection_time_label.props.xalign = 0.0 - label_alignment = self._add_widget_with_padding( \ - self.connection_time_label) - self.info_box.pack_start(label_alignment) - self.connection_time_label.show() + icon = Icon(icon_name='data-download', icon_size=gtk.ICON_SIZE_MENU) + self.connection_info_box.pack_start(icon) + icon.show() + self._data_label_down = gtk.Label() + self._data_label_down.props.xalign = 0.0 + label_alignment = self._add_widget_with_padding(self._data_label_down) + self.connection_info_box.pack_start(label_alignment) + self._data_label_down.show() label_alignment.show() + self.info_box.pack_start(self.connection_info_box) + self.info_box.show() self.set_content(self.info_box) + self.set_state(_GSM_STATE_NOT_READY) + def _add_widget_with_padding(self, child, xalign=0, yalign=0.5): alignment = gtk.Alignment(xalign=xalign, yalign=yalign, xscale=1, yscale=0.33) @@ -252,31 +271,41 @@ class GsmPalette(Palette): alignment.add(child) return alignment - def set_state(self, state): + def update_state(self, state, reason=0): self._current_state = state - self._update_label_and_text() + self._update_label_and_text(reason) - def _update_label_and_text(self): + def _update_label_and_text(self, reason=0): if self._current_state == _GSM_STATE_NOT_READY: self._toggle_state_item.get_child().set_label('...') self.props.secondary_text = _('Please wait...') elif self._current_state == _GSM_STATE_DISCONNECTED: - self._toggle_state_item.get_child().set_label(_('Connect')) + if not self._failed_connection: + self._toggle_state_item.get_child().set_label(_('Connect')) self.props.secondary_text = _('Disconnected') + icon = Icon(icon_name='dialog-ok', \ + icon_size=gtk.ICON_SIZE_MENU) + self._toggle_state_item.set_image(icon) elif self._current_state == _GSM_STATE_CONNECTING: self._toggle_state_item.get_child().set_label(_('Cancel')) self.props.secondary_text = _('Connecting...') + icon = Icon(icon_name='dialog-cancel', \ + icon_size=gtk.ICON_SIZE_MENU) + self._toggle_state_item.set_image(icon) elif self._current_state == _GSM_STATE_CONNECTED: + self._failed_connection = False self._toggle_state_item.get_child().set_label(_('Disconnect')) - self.props.secondary_text = _('Connected') - - elif self._current_state == _GSM_STATE_NEED_AUTH: - self._toggle_state_item.get_child().set_label(_('Sim requires Pin/Puk')) - self.props.secondary_text = _('Authentication Error') + self.update_connection_time() + icon = Icon(icon_name='media-eject', \ + icon_size=gtk.ICON_SIZE_MENU) + self._toggle_state_item.set_image(icon) + elif self._current_state == _GSM_STATE_FAILED: + message_error = self._get_error_by_nm_reason(reason) + self.add_alert(message_error[0], message_error[1]) else: raise ValueError('Invalid GSM state while updating label and ' \ 'text, %s' % str(self._current_state)) @@ -285,17 +314,60 @@ class GsmPalette(Palette): if self._current_state == _GSM_STATE_NOT_READY: pass elif self._current_state == _GSM_STATE_DISCONNECTED: + self.error_title_label.hide() + self.error_description_label.hide() self.emit('gsm-connect') elif self._current_state == _GSM_STATE_CONNECTING: self.emit('gsm-disconnect') elif self._current_state == _GSM_STATE_CONNECTED: self.emit('gsm-disconnect') - elif self._current_state == _GSM_STATE_NEED_AUTH: - self.emit('gsm-disconnect') else: raise ValueError('Invalid GSM state while emitting signal, %s' % \ str(self._current_state)) + def add_alert(self, error, suggestion): + self._failed_connection = True + self._toggle_state_item.get_child().set_label(_('Try connection again')) + + title = _('Error: %s') % error + self.error_title_label.set_markup('%s' % title) + self.error_title_label.show() + + message = _('Suggestion: %s') % suggestion + self.error_description_label.set_text(message) + self.error_description_label.show() + + def update_connection_time(self, connection_time=None): + if connection_time is not None: + self.props.secondary_text = _('Connected for %s') % \ + connection_time.strftime('%H:%M:%S') + else: + self.props.secondary_text = _('Connected for %s') % '00:00:00' + + def update_stats(self, in_bytes, out_bytes): + in_KBytes = in_bytes / 1024 + out_KBytes = out_bytes / 1024 + self._data_label_up.set_text(_("%d KB") % (out_KBytes)) + self._data_label_down.set_text(_("%d KB") % (in_KBytes)) + + def _get_error_by_nm_reason(self, reason): + if reason in [network.NM_DEVICE_STATE_REASON_NO_SECRETS, + network.NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED]: + message = _('Check your Pin/Puk configuration.') + elif reason in [network.NM_DEVICE_STATE_REASON_PPP_DISCONNECT, + network.NM_DEVICE_STATE_REASON_PPP_FAILED]: + message = _('Check your Access Point Name ' \ + '(APN) configuration') + elif reason in [network.NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER, + network.NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT]: + message = _('Check the Number configuration.') + elif reason == network.NM_DEVICE_STATE_REASON_CONFIG_FAILED: + message = _('Check your configuration.') + else: + message = '' + message_tuple = (network.get_error_by_reason(reason), message) + return message_tuple + class WirelessDeviceView(ToolButton): @@ -704,6 +776,10 @@ class GsmDeviceView(TrayIcon): '/', reply_handler=self.__connect_cb, error_handler=self.__connect_error_cb) + else: + self._palette.add_alert(_('No GSM connection available.'), \ + _('Create a connection in the ' \ + 'control panel.')) def __connect_cb(self, active_connection): logging.debug('Connected successfully to gsm device, %s', @@ -736,16 +812,17 @@ class GsmDeviceView(TrayIcon): raise RuntimeError('Error when disconnecting gsm device, %s' % error) def __state_changed_cb(self, new_state, old_state, reason): - logging.debug('State: %s to %s, reason %s', old_state, new_state, reason) - self._update_state(int(new_state)) + logging.debug('State: %s to %s, reason %s', old_state, + new_state, reason) + self._update_state(int(new_state), int(old_state), int(reason)) def __current_state_check_cb(self, properties): - self._update_state(int(properties['State'])) + self._update_state(int(properties['State']), 0, 0) def __current_state_check_error_cb(self, error): raise RuntimeError('Error when checking gsm device state, %s' % error) - def _update_state(self, state): + def _update_state(self, state, old_state, reason): gsm_state = None if state is network.DEVICE_STATE_ACTIVATED: @@ -753,20 +830,22 @@ class GsmDeviceView(TrayIcon): connection = network.find_gsm_connection() if connection is not None: connection.set_connected() - self._connection_timestamp = time.time() - \ + self._connection_timestamp = time.time() - \ connection.get_settings().connection.timestamp self._connection_time_handler = gobject.timeout_add_seconds( \ 1, self.__connection_timecount_cb) - self._update_stats(0, 0) - self._update_connection_time() - self._palette.info_box.show() + self._palette.update_connection_time() + self._palette.update_stats(0, 0) + if self._palette is not None: + self._palette.connection_info_box.show() elif state is network.DEVICE_STATE_DISCONNECTED: gsm_state = _GSM_STATE_DISCONNECTED self._connection_timestamp = 0 if self._connection_time_handler is not None: gobject.source_remove(self._connection_time_handler) - self._palette.info_box.hide() + if self._palette is not None: + self._palette.connection_info_box.hide() elif state in [network.DEVICE_STATE_UNMANAGED, network.DEVICE_STATE_UNAVAILABLE, @@ -775,14 +854,15 @@ class GsmDeviceView(TrayIcon): elif state in [network.DEVICE_STATE_PREPARE, network.DEVICE_STATE_CONFIG, - network.DEVICE_STATE_IP_CONFIG]: + network.DEVICE_STATE_IP_CONFIG, + network.DEVICE_STATE_NEED_AUTH]: gsm_state = _GSM_STATE_CONNECTING - - elif state in [network.DEVICE_STATE_NEED_AUTH]: - gsm_state = _GSM_STATE_NEED_AUTH - + + elif state == network.DEVICE_STATE_FAILED: + gsm_state = _GSM_STATE_FAILED + if self._palette is not None: - self._palette.set_state(gsm_state) + self._palette.update_state(gsm_state, reason) def disconnect(self): self._bus.remove_signal_receiver(self.__state_changed_cb, @@ -791,24 +871,15 @@ class GsmDeviceView(TrayIcon): dbus_interface=_NM_DEVICE_IFACE) def __ppp_stats_changed_cb(self, in_bytes, out_bytes): - self._update_stats(in_bytes, out_bytes) - - def _update_stats(self, in_bytes, out_bytes): - in_KBytes = in_bytes / 1024 - out_KBytes = out_bytes / 1024 - text = _("Data sent %d KB / received %d KB") % (out_KBytes, in_KBytes) - self._palette.data_label.set_text(text) + self._palette.update_stats(in_bytes, out_bytes) def __connection_timecount_cb(self): self._connection_timestamp = self._connection_timestamp + 1 - self._update_connection_time() + connection_time = \ + datetime.datetime.fromtimestamp(self._connection_timestamp) + self._palette.update_connection_time(connection_time) return True - def _update_connection_time(self): - connection_time = datetime.datetime.fromtimestamp( \ - self._connection_timestamp) - text = _("Connection time ") + connection_time.strftime('%H : %M : %S') - self._palette.connection_time_label.set_text(text) class WirelessDeviceObserver(object): def __init__(self, device, tray): diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py index cd0d46e..66d86d8 100644 --- a/src/jarabe/model/network.py +++ b/src/jarabe/model/network.py @@ -18,6 +18,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +from gettext import gettext as _ import logging import os import time @@ -55,6 +56,49 @@ NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0 NM_ACTIVE_CONNECTION_STATE_ACTIVATING = 1 NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2 + +NM_DEVICE_STATE_REASON_UNKNOWN = 0 +NM_DEVICE_STATE_REASON_NONE = 1 +NM_DEVICE_STATE_REASON_NOW_MANAGED = 2 +NM_DEVICE_STATE_REASON_NOW_UNMANAGED = 3 +NM_DEVICE_STATE_REASON_CONFIG_FAILED = 4 +NM_DEVICE_STATE_REASON_CONFIG_UNAVAILABLE = 5 +NM_DEVICE_STATE_REASON_CONFIG_EXPIRED = 6 +NM_DEVICE_STATE_REASON_NO_SECRETS = 7 +NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8 +NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED = 9 +NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED = 10 +NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT = 11 +NM_DEVICE_STATE_REASON_PPP_START_FAILED = 12 +NM_DEVICE_STATE_REASON_PPP_DISCONNECT = 13 +NM_DEVICE_STATE_REASON_PPP_FAILED = 14 +NM_DEVICE_STATE_REASON_DHCP_START_FAILED = 15 +NM_DEVICE_STATE_REASON_DHCP_ERROR = 16 +NM_DEVICE_STATE_REASON_DHCP_FAILED = 17 +NM_DEVICE_STATE_REASON_SHARED_START_FAILED = 18 +NM_DEVICE_STATE_REASON_SHARED_FAILED = 19 +NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED = 20 +NM_DEVICE_STATE_REASON_AUTOIP_ERROR = 21 +NM_DEVICE_STATE_REASON_AUTOIP_FAILED = 22 +NM_DEVICE_STATE_REASON_MODEM_BUSY = 23 +NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE = 24 +NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER = 25 +NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT = 26 +NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED = 27 +NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED = 28 +NM_DEVICE_STATE_REASON_GSM_APN_FAILED = 29 +NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING = 30 +NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED = 31 +NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT = 32 +NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED = 33 +NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED = 34 +NM_DEVICE_STATE_REASON_FIRMWARE_MISSING = 35 +NM_DEVICE_STATE_REASON_REMOVED = 36 +NM_DEVICE_STATE_REASON_SLEEPING = 37 +NM_DEVICE_STATE_REASON_CONNECTION_REMOVED = 38 +NM_DEVICE_STATE_REASON_USER_REQUESTED = 39 +NM_DEVICE_STATE_REASON_CARRIER = 40 + NM_802_11_AP_FLAGS_NONE = 0x00000000 NM_802_11_AP_FLAGS_PRIVACY = 0x00000001 @@ -100,6 +144,103 @@ GSM_PUK_PATH = '/desktop/sugar/network/gsm/puk' _nm_settings = None _conn_counter = 0 +_nm_device_state_reason_description = None + +def get_error_by_reason(reason): + global _nm_device_state_reason_description + + if _nm_device_state_reason_description is None: + _nm_device_state_reason_description = { + NM_DEVICE_STATE_REASON_UNKNOWN: \ + _("The reason for the device state change \ is unknown."), + NM_DEVICE_STATE_REASON_NONE: \ + _("The state change is normal."), + NM_DEVICE_STATE_REASON_NOW_MANAGED: \ + _("The device is now managed."), + NM_DEVICE_STATE_REASON_NOW_UNMANAGED: \ + _("The device is no longer managed."), + NM_DEVICE_STATE_REASON_CONFIG_FAILED: \ + _("The device could not be readied for configuration."), + NM_DEVICE_STATE_REASON_CONFIG_UNAVAILABLE : \ + _("IP configuration could not be reserved " \ + "(no available address, timeout, etc)."), + NM_DEVICE_STATE_REASON_CONFIG_EXPIRED : \ + _("The IP configuration is no longer valid."), + NM_DEVICE_STATE_REASON_NO_SECRETS : \ + _("Secrets were required, but not provided."), + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT : \ + _("The 802.1X supplicant disconnected from "\ + "the access point or authentication server."), + NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED : \ + _("Configuration of the 802.1X supplicant failed."), + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED : \ + _("The 802.1X supplicant quit or failed unexpectedly."), + NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT : \ + _("The 802.1X supplicant took too long to authenticate."), + NM_DEVICE_STATE_REASON_PPP_START_FAILED : \ + _("The PPP service failed to start within the allowed time."), + NM_DEVICE_STATE_REASON_PPP_DISCONNECT : \ + _("The PPP service disconnected unexpectedly."), + NM_DEVICE_STATE_REASON_PPP_FAILED : \ + _("The PPP service quit or failed unexpectedly."), + NM_DEVICE_STATE_REASON_DHCP_START_FAILED : \ + _("The DHCP service failed to start within the allowed time."), + NM_DEVICE_STATE_REASON_DHCP_ERROR : \ + _("The DHCP service reported an unexpected error."), + NM_DEVICE_STATE_REASON_DHCP_FAILED : \ + _("The DHCP service quit or failed unexpectedly."), + NM_DEVICE_STATE_REASON_SHARED_START_FAILED : \ + _("The shared connection service failed to start."), + NM_DEVICE_STATE_REASON_SHARED_FAILED : \ + _("The shared connection service quit or " \ + "failed unexpectedly."), + NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED : \ + _("The AutoIP service failed to start."), + NM_DEVICE_STATE_REASON_AUTOIP_ERROR : \ + _("The AutoIP service reported an unexpected error."), + NM_DEVICE_STATE_REASON_AUTOIP_FAILED : \ + _("The AutoIP service quit or failed unexpectedly."), + NM_DEVICE_STATE_REASON_MODEM_BUSY : \ + _("Dialing failed because the line was busy."), + NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE : \ + _("Dialing failed because there was no dial tone."), + NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER : \ + _("Dialing failed because there was no carrier."), + NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT : \ + _("Dialing timed out."), + NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED : \ + _("Dialing failed."), + NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED : \ + _("Modem initialization failed."), + NM_DEVICE_STATE_REASON_GSM_APN_FAILED : \ + _("Failed to select the specified GSM APN."), + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING : \ + _("Not searching for networks."), + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED : \ + _("Network registration was denied."), + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT : \ + _("Network registration timed out."), + NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED : \ + _("Failed to register with the requested GSM network."), + NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED : \ + _("PIN check failed."), + NM_DEVICE_STATE_REASON_FIRMWARE_MISSING : \ + _("Necessary firmware for the device may be missing."), + NM_DEVICE_STATE_REASON_REMOVED : \ + _("The device was removed."), + NM_DEVICE_STATE_REASON_SLEEPING : \ + _("NetworkManager went to sleep."), + NM_DEVICE_STATE_REASON_CONNECTION_REMOVED : \ + _("The device's active connection was removed " \ + "or disappeared."), + NM_DEVICE_STATE_REASON_USER_REQUESTED : \ + _("A user or client requested the disconnection."), + NM_DEVICE_STATE_REASON_CARRIER : \ + _("The device's carrier/link changed.")} + + return _nm_device_state_reason_description[reason] + + def frequency_to_channel(frequency): """Returns the channel matching a given radio channel frequency. If a @@ -480,17 +621,22 @@ class NMSettingsConnection(dbus.service.Object): in_signature='sasb', out_signature='a{sa{sv}}') def GetSecrets(self, setting_name, hints, request_new, reply, error): logging.debug('Secrets requested for connection %s request_new=%s', - self.path, request_new) - if request_new or self._secrets is None: - # request_new is for example the case when the pw on the AP changes - response = SecretsResponse(self, reply, error) - try: - self.secrets_request.send(self, response=response) - except Exception: - logging.exception('Error requesting the secrets via dialog') + self.path, request_new) + if self._settings.connection.type is not NM_CONNECTION_TYPE_GSM: + if request_new or self._secrets is None: + # request_new is for example the case when the pw on the AP changes + response = SecretsResponse(self, reply, error) + try: + self.secrets_request.send(self, response=response) + except Exception: + logging.exception('Error requesting the secrets via dialog') + else: + reply(self._secrets.get_dict()) else: - reply(self._secrets.get_dict()) - + if not request_new: + reply(self._secrets.get_dict()) + else: + raise Exception('The stored GSM secret has already been supplied ') class AccessPoint(gobject.GObject): __gsignals__ = { -- cgit v0.9.1