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