# 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 subprocess import pm_errors import state_machine from autotest_lib.client.cros.cellular import mm1_constants class ConnectMachine(state_machine.StateMachine): """ ConnectMachine handles the state transitions involved in bringing the modem to the CONNECTED state. """ 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): """ Overriden from superclass. """ logging.info('ConnectMachine: Canceling connect.') super(ConnectMachine, self).Cancel() state = self._modem.Get(mm1_constants.I_MODEM, 'State') reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED if state == mm1_constants.MM_MODEM_STATE_CONNECTING: logging.info('ConnectMachine: Setting state to REGISTERED.') self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_REGISTERED, reason) elif self.enable_initiated and self._modem.enable_step: self._modem.enable_step.Cancel() 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(pm_errors.MMCoreError( pm_errors.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(pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED, message)) return False else: logging.info('ConnectMachine: Waiting for Register.') if not self._modem.IsPendingRegister(): self._modem.RegisterWithNetwork( "", self._return_cb, self._raise_cb) 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_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_CONNECTING, reason) return True def _GetBearerToActivate(self): # Import modem here to avoid circular imports. import modem 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.info(('ConnectMachine: No matching bearer found, ' 'creating brearer with properties: ' + str(self.connect_props))) bearer_path = self._modem.CreateBearer(self.connect_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: Setting state to CONNECTED.') reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED self._modem.ChangeState(mm1_constants.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 (pm_errors.MMError, subprocess.CalledProcessError) as e: logging.error('ConnectMachine: Failed to connect: ' + str(e)) self.raise_cb(e) self._modem.ChangeState( mm1_constants.MM_MODEM_STATE_REGISTERED, mm1_constants.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN) self._modem.connect_step = None return False def _GetModemStateFunctionMap(self): return { mm1_constants.MM_MODEM_STATE_DISABLED: ConnectMachine._HandleDisabledState, mm1_constants.MM_MODEM_STATE_ENABLING: ConnectMachine._HandleEnablingState, mm1_constants.MM_MODEM_STATE_ENABLED: ConnectMachine._HandleEnabledState, mm1_constants.MM_MODEM_STATE_SEARCHING: ConnectMachine._HandleSearchingState, mm1_constants.MM_MODEM_STATE_REGISTERED: ConnectMachine._HandleRegisteredState, mm1_constants.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(pm_errors.MMCoreError( pm_errors.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( pm_errors.MMCoreError(pm_errors.MMCoreError.WRONG_STATE, message)) return False state = self._modem.Get(mm1_constants.I_MODEM, 'State') if state == mm1_constants.MM_MODEM_STATE_CONNECTED: message = 'Modem is already connected.' logging.error(message) self.raise_cb( pm_errors.MMCoreError(pm_errors.MMCoreError.CONNECTED, message)) return False if state == mm1_constants.MM_MODEM_STATE_DISCONNECTING: assert self._modem.IsPendingDisconnect() message = 'Cannot connect while disconnecting.' logging.error(message) self.raise_cb( pm_errors.MMCoreError(pm_errors.MMCoreError.WRONG_STATE, message)) return False logging.info('Starting Connect.') self._modem.connect_step = self return True