1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import subprocess 7 8import pm_errors 9import state_machine 10 11from autotest_lib.client.cros.cellular import mm1_constants 12 13class ConnectMachine(state_machine.StateMachine): 14 """ 15 ConnectMachine handles the state transitions involved in bringing the modem 16 to the CONNECTED state. 17 18 """ 19 def __init__(self, modem, properties, return_cb, raise_cb): 20 super(ConnectMachine, self).__init__(modem) 21 self.connect_props = properties 22 self.return_cb = return_cb 23 self.raise_cb = raise_cb 24 self.enable_initiated = False 25 self.register_initiated = False 26 27 28 def Cancel(self): 29 """ Overriden from superclass. """ 30 logging.info('ConnectMachine: Canceling connect.') 31 super(ConnectMachine, self).Cancel() 32 state = self._modem.Get(mm1_constants.I_MODEM, 'State') 33 reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED 34 if state == mm1_constants.MM_MODEM_STATE_CONNECTING: 35 logging.info('ConnectMachine: Setting state to REGISTERED.') 36 self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_REGISTERED, 37 reason) 38 elif self.enable_initiated and self._modem.enable_step: 39 self._modem.enable_step.Cancel() 40 self._modem.connect_step = None 41 42 43 def _HandleDisabledState(self): 44 logging.info('ConnectMachine: Modem is DISABLED.') 45 assert not self._modem.IsPendingEnable() 46 if self.enable_initiated: 47 message = 'ConnectMachine: Failed to enable modem.' 48 logging.error(message) 49 self.Cancel() 50 self._modem.connect_step = None 51 self.raise_cb(pm_errors.MMCoreError( 52 pm_errors.MMCoreError.FAILED, message)) 53 return False 54 else: 55 logging.info('ConnectMachine: Initiating Enable.') 56 self.enable_initiated = True 57 self._modem.Enable(True) 58 59 # state machine will spin until modem gets enabled, 60 # or if enable fails 61 return True 62 63 64 def _HandleEnablingState(self): 65 logging.info('ConnectMachine: Modem is ENABLING.') 66 assert self._modem.IsPendingEnable() 67 logging.info('ConnectMachine: Waiting for enable.') 68 return True 69 70 71 def _HandleEnabledState(self): 72 logging.info('ConnectMachine: Modem is ENABLED.') 73 74 # Check to see if a register is going on, if not, 75 # start register 76 if self.register_initiated: 77 message = 'ConnectMachine: Failed to register.' 78 logging.error(message) 79 self.Cancel() 80 self._modem.connect_step = None 81 self.raise_cb(pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED, 82 message)) 83 return False 84 else: 85 logging.info('ConnectMachine: Waiting for Register.') 86 if not self._modem.IsPendingRegister(): 87 self._modem.RegisterWithNetwork( 88 "", self._return_cb, self._raise_cb) 89 self.register_initiated = True 90 return True 91 92 93 def _HandleSearchingState(self): 94 logging.info('ConnectMachine: Modem is SEARCHING.') 95 logging.info('ConnectMachine: Waiting for modem to register.') 96 assert self.register_initiated 97 assert self._modem.IsPendingRegister() 98 return True 99 100 101 def _HandleRegisteredState(self): 102 logging.info('ConnectMachine: Modem is REGISTERED.') 103 assert not self._modem.IsPendingDisconnect() 104 assert not self._modem.IsPendingEnable() 105 assert not self._modem.IsPendingDisable() 106 assert not self._modem.IsPendingRegister() 107 logging.info('ConnectMachine: Setting state to CONNECTING.') 108 reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED 109 self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_CONNECTING, 110 reason) 111 return True 112 113 114 def _GetBearerToActivate(self): 115 # Import modem here to avoid circular imports. 116 import modem 117 bearer = None 118 bearer_path = None 119 bearer_props = {} 120 for p, b in self._modem.bearers.iteritems(): 121 # assemble bearer props 122 for key, val in self.connect_props.iteritems(): 123 if key in modem.ALLOWED_BEARER_PROPERTIES: 124 bearer_props[key] = val 125 if (b.bearer_properties == bearer_props): 126 logging.info('ConnectMachine: Found matching bearer.') 127 bearer = b 128 bearer_path = p 129 break 130 if bearer is None: 131 assert bearer_path is None 132 logging.info(('ConnectMachine: No matching bearer found, ' 133 'creating brearer with properties: ' + 134 str(self.connect_props))) 135 bearer_path = self._modem.CreateBearer(self.connect_props) 136 137 return bearer_path 138 139 140 def _HandleConnectingState(self): 141 logging.info('ConnectMachine: Modem is CONNECTING.') 142 assert not self._modem.IsPendingDisconnect() 143 assert not self._modem.IsPendingEnable() 144 assert not self._modem.IsPendingDisable() 145 assert not self._modem.IsPendingRegister() 146 try: 147 bearer_path = self._GetBearerToActivate() 148 self._modem.ActivateBearer(bearer_path) 149 logging.info('ConnectMachine: Setting state to CONNECTED.') 150 reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED 151 self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_CONNECTED, 152 reason) 153 self._modem.connect_step = None 154 logging.info( 155 'ConnectMachine: Returning bearer path: %s', bearer_path) 156 self.return_cb(bearer_path) 157 except (pm_errors.MMError, subprocess.CalledProcessError) as e: 158 logging.error('ConnectMachine: Failed to connect: ' + str(e)) 159 self.raise_cb(e) 160 self._modem.ChangeState( 161 mm1_constants.MM_MODEM_STATE_REGISTERED, 162 mm1_constants.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN) 163 self._modem.connect_step = None 164 return False 165 166 167 def _GetModemStateFunctionMap(self): 168 return { 169 mm1_constants.MM_MODEM_STATE_DISABLED: 170 ConnectMachine._HandleDisabledState, 171 mm1_constants.MM_MODEM_STATE_ENABLING: 172 ConnectMachine._HandleEnablingState, 173 mm1_constants.MM_MODEM_STATE_ENABLED: 174 ConnectMachine._HandleEnabledState, 175 mm1_constants.MM_MODEM_STATE_SEARCHING: 176 ConnectMachine._HandleSearchingState, 177 mm1_constants.MM_MODEM_STATE_REGISTERED: 178 ConnectMachine._HandleRegisteredState, 179 mm1_constants.MM_MODEM_STATE_CONNECTING: 180 ConnectMachine._HandleConnectingState 181 } 182 183 184 def _ShouldStartStateMachine(self): 185 if self._modem.connect_step and self._modem.connect_step != self: 186 # There is already a connect operation in progress. 187 message = 'There is already an ongoing connect operation.' 188 logging.error(message) 189 self.raise_cb(pm_errors.MMCoreError( 190 pm_errors.MMCoreError.IN_PROGRESS, message)) 191 return False 192 elif self._modem.connect_step is None: 193 # There is no connect operation going on, cancelled or otherwise. 194 if self._modem.IsPendingDisable(): 195 message = 'Modem is currently being disabled. Ignoring ' \ 196 'connect.' 197 logging.error(message) 198 self.raise_cb( 199 pm_errors.MMCoreError(pm_errors.MMCoreError.WRONG_STATE, 200 message)) 201 return False 202 state = self._modem.Get(mm1_constants.I_MODEM, 'State') 203 if state == mm1_constants.MM_MODEM_STATE_CONNECTED: 204 message = 'Modem is already connected.' 205 logging.error(message) 206 self.raise_cb( 207 pm_errors.MMCoreError(pm_errors.MMCoreError.CONNECTED, 208 message)) 209 return False 210 if state == mm1_constants.MM_MODEM_STATE_DISCONNECTING: 211 assert self._modem.IsPendingDisconnect() 212 message = 'Cannot connect while disconnecting.' 213 logging.error(message) 214 self.raise_cb( 215 pm_errors.MMCoreError(pm_errors.MMCoreError.WRONG_STATE, 216 message)) 217 return False 218 219 logging.info('Starting Connect.') 220 self._modem.connect_step = self 221 return True 222