Home | History | Annotate | Download | only in pseudomodem
      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 
      5 import dbus
      6 import dbus.types
      7 import logging
      8 
      9 import modem
     10 import pm_constants
     11 import pm_errors
     12 import utils
     13 
     14 from autotest_lib.client.cros.cellular import mm1_constants
     15 
     16 class 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