| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import mm1 |
| import modem |
| import pseudomodem |
| import state_machine |
| import subprocess |
| |
| class ConnectMachine(state_machine.StateMachine): |
| def __init__(self, modem, properties, return_cb, raise_cb): |
| super(ConnectMachine, self).__init__(modem) |
| self.connect_props = properties |
| self.return_cb = return_cb |
| self.raise_cb = raise_cb |
| self.enable_initiated = False |
| self.register_initiated = False |
| |
| def Cancel(self): |
| logging.info('ConnectMachine: Canceling connect.') |
| super(ConnectMachine, self).Cancel() |
| state = self._modem.Get(mm1.I_MODEM, 'State') |
| reason = mm1.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED |
| if state == mm1.MM_MODEM_STATE_CONNECTING: |
| logging.info('ConnectMachine: Setting state to REGISTERED.') |
| self._modem.ChangeState(mm1.MM_MODEM_STATE_REGISTERED, reason) |
| self._modem.connect_step = None |
| |
| def _HandleDisabledState(self): |
| logging.info('ConnectMachine: Modem is DISABLED.') |
| assert not self._modem.IsPendingEnable() |
| if self.enable_initiated: |
| message = 'ConnectMachine: Failed to enable modem.' |
| logging.error(message) |
| self.Cancel() |
| self._modem.connect_step = None |
| self.raise_cb(mm1.MMCoreError(mm1.MMCoreError.FAILED, message)) |
| return False |
| else: |
| logging.info('ConnectMachine: Initiating Enable.') |
| self.enable_initiated = True |
| self._modem.Enable(True) |
| |
| # state machine will spin until modem gets enabled, |
| # or if enable fails |
| return True |
| |
| def _HandleEnablingState(self): |
| logging.info('ConnectMachine: Modem is ENABLING.') |
| assert self._modem.IsPendingEnable() |
| logging.info('ConnectMachine: Waiting for enable.') |
| return True |
| |
| def _HandleEnabledState(self): |
| logging.info('ConnectMachine: Modem is ENABLED.') |
| |
| # Check to see if a register is going on, if not, |
| # start register |
| if self.register_initiated: |
| message = 'ConnectMachine: Failed to register.' |
| logging.error(message) |
| self.Cancel() |
| self._modem.connect_step = None |
| self.raise_cb(mm1.MMCoreError(mm1.MMCoreError.FAILED, message)) |
| return False |
| else: |
| logging.info('ConnectMachine: Waiting for Register.') |
| if not self._modem.IsPendingRegister(): |
| try: |
| self.RegisterWithNetwork() |
| except Exception as e: |
| self.raise_cb(e) |
| return False |
| self.register_initiated = True |
| return True |
| |
| def _HandleSearchingState(self): |
| logging.info('ConnectMachine: Modem is SEARCHING.') |
| logging.info('ConnectMachine: Waiting for modem to register.') |
| assert self.register_initiated |
| assert self._modem.IsPendingRegister() |
| return True |
| |
| def _HandleRegisteredState(self): |
| logging.info('ConnectMachine: Modem is REGISTERED.') |
| assert not self._modem.IsPendingDisconnect() |
| assert not self._modem.IsPendingEnable() |
| assert not self._modem.IsPendingDisable() |
| assert not self._modem.IsPendingRegister() |
| logging.info('ConnectMachine: Setting state to CONNECTING.') |
| reason = mm1.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED |
| self._modem.ChangeState(mm1.MM_MODEM_STATE_CONNECTING, reason) |
| return True |
| |
| def _GetBearerToActivate(self): |
| bearer = None |
| bearer_path = None |
| bearer_props = {} |
| for p, b in self._modem.bearers.iteritems(): |
| # assemble bearer props |
| for key, val in self.connect_props.iteritems(): |
| if key in modem.ALLOWED_BEARER_PROPERTIES: |
| bearer_props[key] = val |
| if (b.bearer_properties == bearer_props): |
| logging.info('ConnectMachine: Found matching bearer.') |
| bearer = b |
| bearer_path = p |
| break |
| if bearer is None: |
| assert bearer_path is None |
| logging.error(('ConnectMachine: No matching bearer found, ' |
| 'creating brearer with properties: ' + |
| str(self.connect_props))) |
| bearer_path = self._modem.CreateBearer(bearer_props) |
| return bearer_path |
| |
| def _HandleConnectingState(self): |
| logging.info('ConnectMachine: Modem is CONNECTING.') |
| assert not self._modem.IsPendingDisconnect() |
| assert not self._modem.IsPendingEnable() |
| assert not self._modem.IsPendingDisable() |
| assert not self._modem.IsPendingRegister() |
| try: |
| bearer_path = self._GetBearerToActivate() |
| self._modem.ActivateBearer(bearer_path) |
| logging.info('ConnectMachine: Restarting DHCP server.') |
| pseudomodem.virtual_ethernet_interface.RestartDHCPServer() |
| logging.info('ConnectMachine: Setting state to CONNECTED.') |
| reason = mm1.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED |
| self._modem.ChangeState(mm1.MM_MODEM_STATE_CONNECTED, reason) |
| self._modem.connect_step = None |
| logging.info( |
| 'ConnectMachine: Returning bearer path: %s', bearer_path) |
| self.return_cb(bearer_path) |
| except (mm1.MMError, subprocess.CalledProcessError) as e: |
| logging.error('ConnectMachine: Failed to connect: ' + str(e)) |
| self.raise_cb(e) |
| self._modem.ChangeState(mm1.MM_MODEM_STATE_REGISTERED, |
| mm1.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN) |
| self._modem.connect_step = None |
| return False |
| |
| def _GetModemStateFunctionMap(self): |
| return { |
| mm1.MM_MODEM_STATE_DISABLED: ConnectMachine._HandleDisabledState, |
| mm1.MM_MODEM_STATE_ENABLING: ConnectMachine._HandleEnablingState, |
| mm1.MM_MODEM_STATE_ENABLED: ConnectMachine._HandleEnabledState, |
| mm1.MM_MODEM_STATE_SEARCHING: ConnectMachine._HandleSearchingState, |
| mm1.MM_MODEM_STATE_REGISTERED: |
| ConnectMachine._HandleRegisteredState, |
| mm1.MM_MODEM_STATE_CONNECTING: ConnectMachine._HandleConnectingState |
| } |
| |
| def _ShouldStartStateMachine(self): |
| if self._modem.connect_step and self._modem.connect_step != self: |
| # There is already a connect operation in progress. |
| message = 'There is already an ongoing connect operation.' |
| logging.error(message) |
| self.raise_cb(mm1.MMCoreError(mm1.MMCoreError.IN_PROGRESS, message)) |
| return False |
| elif self._modem.connect_step is None: |
| # There is no connect operation going on, cancelled or otherwise. |
| if self._modem.IsPendingDisable(): |
| message = 'Modem is currently being disabled. Ignoring ' \ |
| 'connect.' |
| logging.error(message) |
| self.raise_cb( |
| mm1.MMCoreError(mm1.MMCoreError.WRONG_STATE, message)) |
| return False |
| state = self._modem.Get(mm1.I_MODEM, 'State') |
| if state == mm1.MM_MODEM_STATE_CONNECTED: |
| message = 'Modem is already connected.' |
| logging.error(message) |
| self.raise_cb( |
| mm1.MMCoreError(mm1.MMCoreError.CONNECTED, message)) |
| return False |
| if state == mm1.MM_MODEM_STATE_DISCONNECTING: |
| assert self._modem.IsPendingDisconnect() |
| message = 'Cannot connect while disconnecting.' |
| logging.error(message) |
| self.raise_cb( |
| mm1.MMCoreError(mm1.MMCoreError.WRONG_STATE, message)) |
| return False |
| |
| logging.info('Starting Connect.') |
| self._modem.connect_step = self |
| return True |