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 dbus 6import dbus.types 7import logging 8 9import modem 10import pm_constants 11import pm_errors 12import utils 13 14from autotest_lib.client.cros.cellular import mm1_constants 15 16class Modem3gpp(modem.Modem): 17 """ 18 Pseudomodem implementation of the 19 org.freedesktop.ModemManager1.Modem.Modem3gpp and 20 org.freedesktop.ModemManager1.Modem.Simple interfaces. This class provides 21 access to specific actions that may be performed in modems with 3GPP 22 capabilities. 23 24 """ 25 26 IMEI = '00112342342123' 27 28 class GsmNetwork(object): 29 """ 30 GsmNetwork stores the properties of a 3GPP network that can be 31 discovered during a network scan. 32 33 """ 34 def __init__(self, 35 operator_long, 36 operator_short, 37 operator_code, 38 status, 39 access_technology): 40 self.status = status 41 self.operator_long = operator_long 42 self.operator_short = operator_short 43 self.operator_code = operator_code 44 self.access_technology = access_technology 45 46 47 def ToScanDictionary(self): 48 """ 49 @returns: Dictionary containing operator data as defined by 50 org.freedesktop.ModemManager1.Modem.Modem3gpp.Scan. 51 52 """ 53 return { 54 'status': dbus.types.UInt32(self.status), 55 'operator-long': self.operator_long, 56 'operator-short': self.operator_short, 57 'operator-code': self.operator_code, 58 'access-technology': dbus.types.UInt32(self.access_technology), 59 } 60 61 62 def __init__(self, 63 state_machine_factory=None, 64 bus=None, 65 device='pseudomodem0', 66 index=0, 67 roaming_networks=None, 68 config=None): 69 modem.Modem.__init__(self, 70 state_machine_factory, 71 bus=bus, 72 device=device, 73 roaming_networks=roaming_networks, 74 config=config) 75 76 self._scanned_networks = {} 77 self._cached_pco_value = '' 78 self._cached_unregistered_subscription_state = ( 79 mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN) 80 self._cached_registered_subscription_state = ( 81 mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED) 82 83 84 def _InitializeProperties(self): 85 ip = modem.Modem._InitializeProperties(self) 86 props = ip[mm1_constants.I_MODEM] 87 props3gpp = self._GetDefault3GPPProperties() 88 if props3gpp: 89 ip[mm1_constants.I_MODEM_3GPP] = props3gpp 90 props['SupportedCapabilities'] = [ 91 dbus.types.UInt32(mm1_constants.MM_MODEM_CAPABILITY_GSM_UMTS), 92 dbus.types.UInt32(mm1_constants.MM_MODEM_CAPABILITY_LTE), 93 dbus.types.UInt32( 94 mm1_constants.MM_MODEM_CAPABILITY_GSM_UMTS | 95 mm1_constants.MM_MODEM_CAPABILITY_LTE) 96 ] 97 props['CurrentCapabilities'] = dbus.types.UInt32( 98 mm1_constants.MM_MODEM_CAPABILITY_GSM_UMTS | 99 mm1_constants.MM_MODEM_CAPABILITY_LTE) 100 props['MaxBearers'] = dbus.types.UInt32(3) 101 props['MaxActiveBearers'] = dbus.types.UInt32(2) 102 props['EquipmentIdentifier'] = self.IMEI 103 props['AccessTechnologies'] = dbus.types.UInt32(( 104 mm1_constants.MM_MODEM_ACCESS_TECHNOLOGY_GSM | 105 mm1_constants.MM_MODEM_ACCESS_TECHNOLOGY_UMTS)) 106 props['SupportedModes'] = [ 107 dbus.types.Struct( 108 [dbus.types.UInt32(mm1_constants.MM_MODEM_MODE_3G | 109 mm1_constants.MM_MODEM_MODE_4G), 110 dbus.types.UInt32(mm1_constants.MM_MODEM_MODE_4G)], 111 signature='uu') 112 ] 113 props['CurrentModes'] = props['SupportedModes'][0] 114 props['SupportedBands'] = [ 115 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_EGSM), 116 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_DCS), 117 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_PCS), 118 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_G850), 119 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U2100), 120 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U1800), 121 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U17IV), 122 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U800), 123 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U850) 124 ] 125 props['CurrentBands'] = [ 126 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_EGSM), 127 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_DCS), 128 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_PCS), 129 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_G850), 130 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U2100), 131 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U800), 132 dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U850) 133 ] 134 return ip 135 136 137 def _GetDefault3GPPProperties(self): 138 if not self.sim or self.sim.locked: 139 return None 140 return { 141 'Imei' : self.IMEI, 142 'RegistrationState' : ( 143 dbus.types.UInt32( 144 mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)), 145 'OperatorCode' : '', 146 'OperatorName' : '', 147 'EnabledFacilityLocks' : ( 148 dbus.types.UInt32(self.sim.enabled_locks)), 149 'SubscriptionState' : dbus.types.UInt32( 150 mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN), 151 'VendorPcoInfo': '' 152 } 153 154 155 def SyncScan(self): 156 """ The synchronous implementation of |Scan| for this class. """ 157 state = self.Get(mm1_constants.I_MODEM, 'State') 158 if state < mm1_constants.MM_MODEM_STATE_ENABLED: 159 raise pm_errors.MMCoreError( 160 pm_errors.MMCoreError.WRONG_STATE, 161 'Modem not enabled, cannot scan for networks.') 162 163 sim_path = self.Get(mm1_constants.I_MODEM, 'Sim') 164 if not self.sim: 165 assert sim_path == mm1_constants.ROOT_PATH 166 raise pm_errors.MMMobileEquipmentError( 167 pm_errors.MMMobileEquipmentError.SIM_NOT_INSERTED, 168 'Cannot scan for networks because no SIM is inserted.') 169 assert sim_path != mm1_constants.ROOT_PATH 170 171 # TODO(armansito): check here for SIM lock? 172 173 scanned = [network.ToScanDictionary() 174 for network in self.roaming_networks] 175 176 # get home network 177 sim_props = self.sim.GetAll(mm1_constants.I_SIM) 178 scanned.append({ 179 'status': dbus.types.UInt32( 180 mm1_constants.MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE), 181 'operator-long': sim_props['OperatorName'], 182 'operator-short': sim_props['OperatorName'], 183 'operator-code': sim_props['OperatorIdentifier'], 184 'access-technology': dbus.types.UInt32(self.sim.access_technology) 185 }) 186 187 self._scanned_networks = ( 188 {network['operator-code']: network for network in scanned}) 189 return scanned 190 191 192 def AssignPcoValue(self, pco_value): 193 """ 194 Stores the given value so that it is shown as the value of VendorPcoInfo 195 when the modem is in a registered state. 196 197 Always prefer this method over calling "Set" directly if the PCO value 198 should be cached. 199 200 Note: See testing.Testing.UpdatePcoInfo, which allows calling this 201 method over D-Bus. 202 203 @param pco_value: String containing the PCO value to remember. 204 205 """ 206 self._cached_pco_value = pco_value 207 self.UpdatePcoInfo() 208 209 210 def UpdatePcoInfo(self): 211 """ 212 Updates the current PCO value based on the registration state. 213 214 """ 215 if not mm1_constants.I_MODEM_3GPP in self._properties: 216 return 217 state = self.Get(mm1_constants.I_MODEM_3GPP, 'RegistrationState') 218 if (state == mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME or 219 state == mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING): 220 new_pco_value = self._cached_pco_value 221 else: 222 new_pco_value = '' 223 self.Set(mm1_constants.I_MODEM_3GPP, 'VendorPcoInfo', new_pco_value) 224 225 226 def AssignSubscriptionState(self, 227 unregistered_subscription_state, 228 registered_subscription_state): 229 """ 230 Caches the given subscription states and updates the actual 231 |SubscriptionState| property depending on the |RegistrationState|. 232 233 @param unregistered_subscription_state: This subscription state is 234 returned when the modem is not registered on a network. 235 @param registered_subscription_state: This subscription state is 236 returned when the modem is registered on a network. 237 238 """ 239 self._cached_unregistered_subscription_state = ( 240 unregistered_subscription_state) 241 self._cached_registered_subscription_state = ( 242 registered_subscription_state) 243 self.UpdateSubscriptionState() 244 245 246 def UpdateSubscriptionState(self): 247 """ 248 Updates the current |SubscriptionState| property depending on the 249 |RegistrationState|. 250 251 """ 252 if not mm1_constants.I_MODEM_3GPP in self._properties: 253 return 254 registration_state = self.Get(mm1_constants.I_MODEM_3GPP, 255 'RegistrationState') 256 if (registration_state == 257 mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME or 258 registration_state == 259 mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING): 260 new_subscription_state = self._cached_registered_subscription_state 261 else: 262 new_subscription_state = ( 263 self._cached_unregistered_subscription_state) 264 265 self.SetUInt32(mm1_constants.I_MODEM_3GPP, 266 'SubscriptionState', 267 new_subscription_state) 268 269 270 def UpdateLockStatus(self): 271 """ 272 Overloads superclass implementation. Also updates 273 'EnabledFacilityLocks' if 3GPP properties are exposed. 274 275 """ 276 modem.Modem.UpdateLockStatus(self) 277 if mm1_constants.I_MODEM_3GPP in self._properties: 278 self.SetUInt32(mm1_constants.I_MODEM_3GPP, 279 'EnabledFacilityLocks', 280 self.sim.enabled_locks) 281 282 283 def SetSIM(self, sim): 284 """ 285 Overrides modem.Modem.SetSIM. Once the SIM has been assigned, attempts 286 to expose 3GPP properties if SIM readable. 287 288 @param sim: An instance of sim.SIM 289 Emits: 290 PropertiesChanged 291 292 """ 293 modem.Modem.SetSIM(self, sim) 294 self.Expose3GPPProperties() 295 296 297 def Expose3GPPProperties(self): 298 """ 299 A call to this method will attempt to expose 3GPP properties if there 300 is a current SIM and is unlocked. 301 302 """ 303 props = self._GetDefault3GPPProperties() 304 if props: 305 self.SetAll(mm1_constants.I_MODEM_3GPP, props) 306 307 308 def SetRegistrationState(self, state): 309 """ 310 Sets the 'RegistrationState' property. 311 312 @param state: An MMModem3gppRegistrationState value. 313 Emits: 314 PropertiesChanged 315 316 """ 317 self.SetUInt32(mm1_constants.I_MODEM_3GPP, 'RegistrationState', state) 318 self.UpdatePcoInfo() 319 self.UpdateSubscriptionState() 320 321 322 @property 323 def scanned_networks(self): 324 """ 325 @returns: Dictionary containing the result of the most recent network 326 scan, where the keys are the operator code. 327 328 """ 329 return self._scanned_networks 330 331 332 @utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb') 333 @dbus.service.method(mm1_constants.I_MODEM_3GPP, in_signature='s', 334 async_callbacks=('return_cb', 'raise_cb')) 335 def Register(self, operator_id, return_cb=None, raise_cb=None): 336 """ 337 Request registration with a given modem network. 338 339 @param operator_id: The operator ID to register. An empty string can be 340 used to register to the home network. 341 @param return_cb: Async success callback. 342 @param raise_cb: Async error callback. 343 344 """ 345 logging.info('Modem3gpp.Register: %s', operator_id) 346 347 # Check if we're already registered with the given network. 348 if (self.Get(mm1_constants.I_MODEM_3GPP, 'OperatorCode') == 349 operator_id or 350 ((not operator_id and self.Get(mm1_constants.I_MODEM, 'State') >= 351 mm1_constants.MM_MODEM_STATE_REGISTERED))): 352 message = 'Already registered.' 353 logging.info(message) 354 raise pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED, message) 355 356 if (self.Get(mm1_constants.I_MODEM, 'State') < 357 mm1_constants.MM_MODEM_STATE_ENABLED): 358 message = 'Cannot register the modem if not enabled.' 359 logging.info(message) 360 raise pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED, message) 361 362 self.CancelAllStateMachines() 363 364 def _Reregister(): 365 if (self.Get(mm1_constants.I_MODEM, 'State') == 366 mm1_constants.MM_MODEM_STATE_REGISTERED): 367 self.UnregisterWithNetwork() 368 self.RegisterWithNetwork(operator_id, return_cb, raise_cb) 369 370 if (self.Get(mm1_constants.I_MODEM, 'State') == 371 mm1_constants.MM_MODEM_STATE_CONNECTED): 372 self.Disconnect(mm1_constants.ROOT_PATH, _Reregister, raise_cb) 373 else: 374 _Reregister() 375 376 377 def SetRegistered(self, operator_code, operator_name): 378 """ 379 Sets the modem to be registered with the give network. Sets the Modem 380 and Modem3gpp registration states. 381 382 @param operator_code: The operator code that should be displayed by 383 the modem. 384 @param operator_name: The operator name that should be displayed by 385 the modem. 386 387 """ 388 if operator_code: 389 assert self.sim 390 assert (self.Get(mm1_constants.I_MODEM, 'Sim') != 391 mm1_constants.ROOT_PATH) 392 if (operator_code == 393 self.sim.Get(mm1_constants.I_SIM, 'OperatorIdentifier')): 394 state = mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME 395 else: 396 state = mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING 397 else: 398 state = mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME 399 400 logging.info('Modem3gpp.Register: Setting registration state to %s.', 401 mm1_constants.RegistrationStateToString(state)) 402 self.SetRegistrationState(state) 403 logging.info('Modem3gpp.Register: Setting state to REGISTERED.') 404 self.ChangeState(mm1_constants.MM_MODEM_STATE_REGISTERED, 405 mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED) 406 self.Set(mm1_constants.I_MODEM_3GPP, 'OperatorCode', operator_code) 407 self.Set(mm1_constants.I_MODEM_3GPP, 'OperatorName', operator_name) 408 409 410 @utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb') 411 @dbus.service.method(mm1_constants.I_MODEM_3GPP, out_signature='aa{sv}', 412 async_callbacks=('return_cb', 'raise_cb')) 413 def Scan(self, return_cb, raise_cb): 414 """ 415 Scan for available networks. 416 417 @param return_cb: This function is called with the result. 418 @param raise_cb: This function may be called with error. 419 @returns: An array of dictionaries with each array element describing a 420 mobile network found in the scan. See the ModemManager reference 421 manual for the list of keys that may be included in the returned 422 dictionary. 423 424 """ 425 scan_result = self.SyncScan() 426 return_cb(scan_result) 427 428 429 def RegisterWithNetwork( 430 self, operator_id="", return_cb=None, raise_cb=None): 431 """ 432 Overridden from superclass. 433 434 @param operator_id: See superclass documentation. 435 @param return_cb: See superclass documentation. 436 @param raise_cb: See superclass documentation. 437 438 """ 439 machine = self._state_machine_factory.CreateMachine( 440 pm_constants.STATE_MACHINE_REGISTER, 441 self, 442 operator_id, 443 return_cb, 444 raise_cb) 445 machine.Start() 446 447 448 def UnregisterWithNetwork(self): 449 """ 450 Overridden from superclass. 451 452 """ 453 logging.info('Modem3gpp.UnregisterWithHomeNetwork') 454 logging.info('Setting registration state to IDLE.') 455 self.SetRegistrationState( 456 mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE) 457 logging.info('Setting state to ENABLED.') 458 self.ChangeState(mm1_constants.MM_MODEM_STATE_ENABLED, 459 mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED) 460 self.Set(mm1_constants.I_MODEM_3GPP, 'OperatorName', '') 461 self.Set(mm1_constants.I_MODEM_3GPP, 'OperatorCode', '') 462 463 464 # Inherited from modem_simple.ModemSimple. 465 @utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb') 466 def Connect(self, properties, return_cb, raise_cb): 467 """ 468 Overriden from superclass. 469 470 @param properties 471 @param return_cb 472 @param raise_cb 473 474 """ 475 logging.info('Connect') 476 machine = self._state_machine_factory.CreateMachine( 477 pm_constants.STATE_MACHINE_CONNECT, 478 self, 479 properties, 480 return_cb, 481 raise_cb) 482 machine.Start() 483 484 485 # Inherited from modem_simple.ModemSimple. 486 @utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb') 487 def Disconnect(self, bearer_path, return_cb, raise_cb, *return_cb_args): 488 """ 489 Overriden from superclass. 490 491 @param bearer_path 492 @param return_cb 493 @param raise_cb 494 @param return_cb_args 495 496 """ 497 logging.info('Disconnect: %s', bearer_path) 498 machine = self._state_machine_factory.CreateMachine( 499 pm_constants.STATE_MACHINE_DISCONNECT, 500 self, 501 bearer_path, 502 return_cb, 503 raise_cb, 504 return_cb_args) 505 machine.Start() 506 507 508 # Inherited from modem_simple.ModemSimple. 509 @utils.log_dbus_method() 510 def GetStatus(self): 511 """ 512 Overriden from superclass. 513 514 """ 515 modem_props = self.GetAll(mm1_constants.I_MODEM) 516 m3gpp_props = self.GetAll(mm1_constants.I_MODEM_3GPP) 517 retval = {} 518 retval['state'] = modem_props['State'] 519 if retval['state'] >= mm1_constants.MM_MODEM_STATE_REGISTERED: 520 retval['signal-quality'] = modem_props['SignalQuality'][0] 521 retval['bands'] = modem_props['CurrentBands'] 522 retval['access-technology'] = self.sim.access_technology 523 retval['m3gpp-registration-state'] = \ 524 m3gpp_props['RegistrationState'] 525 retval['m3gpp-operator-code'] = m3gpp_props['OperatorCode'] 526 retval['m3gpp-operator-name'] = m3gpp_props['OperatorName'] 527 return retval 528 # TODO(armansito): implement 529 # org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd, if needed 530 # (in a separate class?) 531