522 lines
19 KiB
Python
522 lines
19 KiB
Python
# 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 dbus
|
|
import dbus.types
|
|
import logging
|
|
|
|
import modem
|
|
import pm_constants
|
|
import pm_errors
|
|
import utils
|
|
|
|
from autotest_lib.client.cros.cellular import mm1_constants
|
|
|
|
def SubscriptionStateToPco(state):
|
|
"""
|
|
Takes an old SubscriptionState enum and returns a Pco that will be
|
|
interpreted as that subscription state.
|
|
|
|
@param state: see mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_*
|
|
"""
|
|
|
|
pco_data = '\x27\x08\x00\xFF\x00\x04\x13\x01\x84'
|
|
if state == mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN:
|
|
pco_data += '\xFF'
|
|
elif state == mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED:
|
|
pco_data += '\x05'
|
|
elif state == mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED:
|
|
pco_data += '\x00'
|
|
elif state == mm1_constants.MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA:
|
|
pco_data += '\x03'
|
|
|
|
return dbus.types.Struct(
|
|
[dbus.types.UInt32(1),
|
|
dbus.types.Boolean(True),
|
|
dbus.types.ByteArray(pco_data)],
|
|
signature='ubay')
|
|
|
|
|
|
class Modem3gpp(modem.Modem):
|
|
"""
|
|
Pseudomodem implementation of the
|
|
org.freedesktop.ModemManager1.Modem.Modem3gpp and
|
|
org.freedesktop.ModemManager1.Modem.Simple interfaces. This class provides
|
|
access to specific actions that may be performed in modems with 3GPP
|
|
capabilities.
|
|
|
|
"""
|
|
|
|
IMEI = '00112342342123'
|
|
|
|
class GsmNetwork(object):
|
|
"""
|
|
GsmNetwork stores the properties of a 3GPP network that can be
|
|
discovered during a network scan.
|
|
|
|
"""
|
|
def __init__(self,
|
|
operator_long,
|
|
operator_short,
|
|
operator_code,
|
|
status,
|
|
access_technology):
|
|
self.status = status
|
|
self.operator_long = operator_long
|
|
self.operator_short = operator_short
|
|
self.operator_code = operator_code
|
|
self.access_technology = access_technology
|
|
|
|
|
|
def ToScanDictionary(self):
|
|
"""
|
|
@returns: Dictionary containing operator data as defined by
|
|
org.freedesktop.ModemManager1.Modem.Modem3gpp.Scan.
|
|
|
|
"""
|
|
return {
|
|
'status': dbus.types.UInt32(self.status),
|
|
'operator-long': self.operator_long,
|
|
'operator-short': self.operator_short,
|
|
'operator-code': self.operator_code,
|
|
'access-technology': dbus.types.UInt32(self.access_technology),
|
|
}
|
|
|
|
|
|
def __init__(self,
|
|
state_machine_factory=None,
|
|
bus=None,
|
|
device='pseudomodem0',
|
|
index=0,
|
|
roaming_networks=None,
|
|
config=None):
|
|
modem.Modem.__init__(self,
|
|
state_machine_factory,
|
|
bus=bus,
|
|
device=device,
|
|
roaming_networks=roaming_networks,
|
|
config=config)
|
|
|
|
self._scanned_networks = {}
|
|
self._cached_pco = dbus.types.Array([], "(ubay)")
|
|
|
|
|
|
def _InitializeProperties(self):
|
|
ip = modem.Modem._InitializeProperties(self)
|
|
props = ip[mm1_constants.I_MODEM]
|
|
props3gpp = self._GetDefault3GPPProperties()
|
|
if props3gpp:
|
|
ip[mm1_constants.I_MODEM_3GPP] = props3gpp
|
|
props['SupportedCapabilities'] = [
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_CAPABILITY_GSM_UMTS),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_CAPABILITY_LTE),
|
|
dbus.types.UInt32(
|
|
mm1_constants.MM_MODEM_CAPABILITY_GSM_UMTS |
|
|
mm1_constants.MM_MODEM_CAPABILITY_LTE)
|
|
]
|
|
props['CurrentCapabilities'] = dbus.types.UInt32(
|
|
mm1_constants.MM_MODEM_CAPABILITY_GSM_UMTS |
|
|
mm1_constants.MM_MODEM_CAPABILITY_LTE)
|
|
props['MaxBearers'] = dbus.types.UInt32(3)
|
|
props['MaxActiveBearers'] = dbus.types.UInt32(2)
|
|
props['EquipmentIdentifier'] = self.IMEI
|
|
props['AccessTechnologies'] = dbus.types.UInt32((
|
|
mm1_constants.MM_MODEM_ACCESS_TECHNOLOGY_GSM |
|
|
mm1_constants.MM_MODEM_ACCESS_TECHNOLOGY_UMTS))
|
|
props['SupportedModes'] = [
|
|
dbus.types.Struct(
|
|
[dbus.types.UInt32(mm1_constants.MM_MODEM_MODE_3G |
|
|
mm1_constants.MM_MODEM_MODE_4G),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_MODE_4G)],
|
|
signature='uu')
|
|
]
|
|
props['CurrentModes'] = props['SupportedModes'][0]
|
|
props['SupportedBands'] = [
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_EGSM),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_DCS),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_PCS),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_G850),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U2100),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U1800),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U17IV),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U800),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U850)
|
|
]
|
|
props['CurrentBands'] = [
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_EGSM),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_DCS),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_PCS),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_G850),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U2100),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U800),
|
|
dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_U850)
|
|
]
|
|
return ip
|
|
|
|
|
|
def _GetDefault3GPPProperties(self):
|
|
if not self.sim or self.sim.locked:
|
|
return None
|
|
return {
|
|
'Imei' : self.IMEI,
|
|
'RegistrationState' : (
|
|
dbus.types.UInt32(
|
|
mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)),
|
|
'OperatorCode' : '',
|
|
'OperatorName' : '',
|
|
'EnabledFacilityLocks' : (
|
|
dbus.types.UInt32(self.sim.enabled_locks)),
|
|
'Pco': dbus.types.Array([], "(ubay)"),
|
|
}
|
|
|
|
|
|
def SyncScan(self):
|
|
""" The synchronous implementation of |Scan| for this class. """
|
|
state = self.Get(mm1_constants.I_MODEM, 'State')
|
|
if state < mm1_constants.MM_MODEM_STATE_ENABLED:
|
|
raise pm_errors.MMCoreError(
|
|
pm_errors.MMCoreError.WRONG_STATE,
|
|
'Modem not enabled, cannot scan for networks.')
|
|
|
|
sim_path = self.Get(mm1_constants.I_MODEM, 'Sim')
|
|
if not self.sim:
|
|
assert sim_path == mm1_constants.ROOT_PATH
|
|
raise pm_errors.MMMobileEquipmentError(
|
|
pm_errors.MMMobileEquipmentError.SIM_NOT_INSERTED,
|
|
'Cannot scan for networks because no SIM is inserted.')
|
|
assert sim_path != mm1_constants.ROOT_PATH
|
|
|
|
# TODO(armansito): check here for SIM lock?
|
|
|
|
scanned = [network.ToScanDictionary()
|
|
for network in self.roaming_networks]
|
|
|
|
# get home network
|
|
sim_props = self.sim.GetAll(mm1_constants.I_SIM)
|
|
scanned.append({
|
|
'status': dbus.types.UInt32(
|
|
mm1_constants.MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE),
|
|
'operator-long': sim_props['OperatorName'],
|
|
'operator-short': sim_props['OperatorName'],
|
|
'operator-code': sim_props['OperatorIdentifier'],
|
|
'access-technology': dbus.types.UInt32(self.sim.access_technology)
|
|
})
|
|
|
|
self._scanned_networks = (
|
|
{network['operator-code']: network for network in scanned})
|
|
return scanned
|
|
|
|
|
|
def AssignPco(self, pco):
|
|
"""
|
|
Stores the given value so that it is shown as the value of Pco when
|
|
the modem is in a registered state.
|
|
|
|
Always prefer this method over calling "Set" directly if the PCO value
|
|
should be cached.
|
|
|
|
Note: See testing.Testing.UpdatePco, which allows calling this method
|
|
over D-Bus.
|
|
|
|
@param pco_value: D-Bus struct containing the PCO value to remember.
|
|
|
|
"""
|
|
self._cached_pco = pco
|
|
self.UpdatePco()
|
|
|
|
|
|
def UpdatePco(self):
|
|
"""
|
|
Updates the current PCO value based on the registration state.
|
|
|
|
"""
|
|
if not mm1_constants.I_MODEM_3GPP in self._properties:
|
|
return
|
|
state = self.Get(mm1_constants.I_MODEM_3GPP, 'RegistrationState')
|
|
if (state == mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME or
|
|
state == mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING):
|
|
new_pco_value = self._cached_pco
|
|
else:
|
|
new_pco_value = dbus.types.Array([], "(ubay)")
|
|
self.Set(mm1_constants.I_MODEM_3GPP, 'Pco', new_pco_value)
|
|
|
|
|
|
def AssignSubscriptionState(self,
|
|
registered_subscription_state):
|
|
"""
|
|
Caches the given subscription states and updates the actual
|
|
|SubscriptionState| property depending on the |RegistrationState|.
|
|
|
|
@param unregistered_subscription_state: This subscription state is
|
|
returned when the modem is not registered on a network.
|
|
@param registered_subscription_state: This subscription state is
|
|
returned when the modem is registered on a network.
|
|
|
|
"""
|
|
new_pco = SubscriptionStateToPco(registered_subscription_state)
|
|
self.AssignPco([new_pco])
|
|
self.UpdatePco()
|
|
|
|
|
|
def UpdateLockStatus(self):
|
|
"""
|
|
Overloads superclass implementation. Also updates
|
|
'EnabledFacilityLocks' if 3GPP properties are exposed.
|
|
|
|
"""
|
|
modem.Modem.UpdateLockStatus(self)
|
|
if mm1_constants.I_MODEM_3GPP in self._properties:
|
|
self.SetUInt32(mm1_constants.I_MODEM_3GPP,
|
|
'EnabledFacilityLocks',
|
|
self.sim.enabled_locks)
|
|
|
|
|
|
def SetSIM(self, sim):
|
|
"""
|
|
Overrides modem.Modem.SetSIM. Once the SIM has been assigned, attempts
|
|
to expose 3GPP properties if SIM readable.
|
|
|
|
@param sim: An instance of sim.SIM
|
|
Emits:
|
|
PropertiesChanged
|
|
|
|
"""
|
|
modem.Modem.SetSIM(self, sim)
|
|
self.Expose3GPPProperties()
|
|
|
|
|
|
def Expose3GPPProperties(self):
|
|
"""
|
|
A call to this method will attempt to expose 3GPP properties if there
|
|
is a current SIM and is unlocked.
|
|
|
|
"""
|
|
props = self._GetDefault3GPPProperties()
|
|
if props:
|
|
self.SetAll(mm1_constants.I_MODEM_3GPP, props)
|
|
|
|
|
|
def SetRegistrationState(self, state):
|
|
"""
|
|
Sets the 'RegistrationState' property.
|
|
|
|
@param state: An MMModem3gppRegistrationState value.
|
|
Emits:
|
|
PropertiesChanged
|
|
|
|
"""
|
|
self.SetUInt32(mm1_constants.I_MODEM_3GPP, 'RegistrationState', state)
|
|
self.UpdatePco()
|
|
|
|
|
|
@property
|
|
def scanned_networks(self):
|
|
"""
|
|
@returns: Dictionary containing the result of the most recent network
|
|
scan, where the keys are the operator code.
|
|
|
|
"""
|
|
return self._scanned_networks
|
|
|
|
|
|
@utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb')
|
|
@dbus.service.method(mm1_constants.I_MODEM_3GPP, in_signature='s',
|
|
async_callbacks=('return_cb', 'raise_cb'))
|
|
def Register(self, operator_id, return_cb=None, raise_cb=None):
|
|
"""
|
|
Request registration with a given modem network.
|
|
|
|
@param operator_id: The operator ID to register. An empty string can be
|
|
used to register to the home network.
|
|
@param return_cb: Async success callback.
|
|
@param raise_cb: Async error callback.
|
|
|
|
"""
|
|
logging.info('Modem3gpp.Register: %s', operator_id)
|
|
|
|
# Check if we're already registered with the given network.
|
|
if (self.Get(mm1_constants.I_MODEM_3GPP, 'OperatorCode') ==
|
|
operator_id or
|
|
((not operator_id and self.Get(mm1_constants.I_MODEM, 'State') >=
|
|
mm1_constants.MM_MODEM_STATE_REGISTERED))):
|
|
message = 'Already registered.'
|
|
logging.info(message)
|
|
raise pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED, message)
|
|
|
|
if (self.Get(mm1_constants.I_MODEM, 'State') <
|
|
mm1_constants.MM_MODEM_STATE_ENABLED):
|
|
message = 'Cannot register the modem if not enabled.'
|
|
logging.info(message)
|
|
raise pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED, message)
|
|
|
|
self.CancelAllStateMachines()
|
|
|
|
def _Reregister():
|
|
if (self.Get(mm1_constants.I_MODEM, 'State') ==
|
|
mm1_constants.MM_MODEM_STATE_REGISTERED):
|
|
self.UnregisterWithNetwork()
|
|
self.RegisterWithNetwork(operator_id, return_cb, raise_cb)
|
|
|
|
if (self.Get(mm1_constants.I_MODEM, 'State') ==
|
|
mm1_constants.MM_MODEM_STATE_CONNECTED):
|
|
self.Disconnect(mm1_constants.ROOT_PATH, _Reregister, raise_cb)
|
|
else:
|
|
_Reregister()
|
|
|
|
|
|
def SetRegistered(self, operator_code, operator_name):
|
|
"""
|
|
Sets the modem to be registered with the give network. Sets the Modem
|
|
and Modem3gpp registration states.
|
|
|
|
@param operator_code: The operator code that should be displayed by
|
|
the modem.
|
|
@param operator_name: The operator name that should be displayed by
|
|
the modem.
|
|
|
|
"""
|
|
if operator_code:
|
|
assert self.sim
|
|
assert (self.Get(mm1_constants.I_MODEM, 'Sim') !=
|
|
mm1_constants.ROOT_PATH)
|
|
if (operator_code ==
|
|
self.sim.Get(mm1_constants.I_SIM, 'OperatorIdentifier')):
|
|
state = mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME
|
|
else:
|
|
state = mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING
|
|
else:
|
|
state = mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME
|
|
|
|
logging.info('Modem3gpp.Register: Setting registration state to %s.',
|
|
mm1_constants.RegistrationStateToString(state))
|
|
self.SetRegistrationState(state)
|
|
logging.info('Modem3gpp.Register: Setting state to REGISTERED.')
|
|
self.ChangeState(mm1_constants.MM_MODEM_STATE_REGISTERED,
|
|
mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED)
|
|
self.Set(mm1_constants.I_MODEM_3GPP, 'OperatorCode', operator_code)
|
|
self.Set(mm1_constants.I_MODEM_3GPP, 'OperatorName', operator_name)
|
|
|
|
|
|
@utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb')
|
|
@dbus.service.method(mm1_constants.I_MODEM_3GPP, out_signature='aa{sv}',
|
|
async_callbacks=('return_cb', 'raise_cb'))
|
|
def Scan(self, return_cb, raise_cb):
|
|
"""
|
|
Scan for available networks.
|
|
|
|
@param return_cb: This function is called with the result.
|
|
@param raise_cb: This function may be called with error.
|
|
@returns: An array of dictionaries with each array element describing a
|
|
mobile network found in the scan. See the ModemManager reference
|
|
manual for the list of keys that may be included in the returned
|
|
dictionary.
|
|
|
|
"""
|
|
scan_result = self.SyncScan()
|
|
return_cb(scan_result)
|
|
|
|
|
|
def RegisterWithNetwork(
|
|
self, operator_id="", return_cb=None, raise_cb=None):
|
|
"""
|
|
Overridden from superclass.
|
|
|
|
@param operator_id: See superclass documentation.
|
|
@param return_cb: See superclass documentation.
|
|
@param raise_cb: See superclass documentation.
|
|
|
|
"""
|
|
machine = self._state_machine_factory.CreateMachine(
|
|
pm_constants.STATE_MACHINE_REGISTER,
|
|
self,
|
|
operator_id,
|
|
return_cb,
|
|
raise_cb)
|
|
machine.Start()
|
|
|
|
|
|
def UnregisterWithNetwork(self):
|
|
"""
|
|
Overridden from superclass.
|
|
|
|
"""
|
|
logging.info('Modem3gpp.UnregisterWithHomeNetwork')
|
|
logging.info('Setting registration state to IDLE.')
|
|
self.SetRegistrationState(
|
|
mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)
|
|
logging.info('Setting state to ENABLED.')
|
|
self.ChangeState(mm1_constants.MM_MODEM_STATE_ENABLED,
|
|
mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED)
|
|
self.Set(mm1_constants.I_MODEM_3GPP, 'OperatorName', '')
|
|
self.Set(mm1_constants.I_MODEM_3GPP, 'OperatorCode', '')
|
|
|
|
|
|
# Inherited from modem_simple.ModemSimple.
|
|
@utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb')
|
|
def Connect(self, properties, return_cb, raise_cb):
|
|
"""
|
|
Overriden from superclass.
|
|
|
|
@param properties
|
|
@param return_cb
|
|
@param raise_cb
|
|
|
|
"""
|
|
logging.info('Connect')
|
|
machine = self._state_machine_factory.CreateMachine(
|
|
pm_constants.STATE_MACHINE_CONNECT,
|
|
self,
|
|
properties,
|
|
return_cb,
|
|
raise_cb)
|
|
machine.Start()
|
|
|
|
|
|
# Inherited from modem_simple.ModemSimple.
|
|
@utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb')
|
|
def Disconnect(self, bearer_path, return_cb, raise_cb, *return_cb_args):
|
|
"""
|
|
Overriden from superclass.
|
|
|
|
@param bearer_path
|
|
@param return_cb
|
|
@param raise_cb
|
|
@param return_cb_args
|
|
|
|
"""
|
|
logging.info('Disconnect: %s', bearer_path)
|
|
machine = self._state_machine_factory.CreateMachine(
|
|
pm_constants.STATE_MACHINE_DISCONNECT,
|
|
self,
|
|
bearer_path,
|
|
return_cb,
|
|
raise_cb,
|
|
return_cb_args)
|
|
machine.Start()
|
|
|
|
|
|
# Inherited from modem_simple.ModemSimple.
|
|
@utils.log_dbus_method()
|
|
def GetStatus(self):
|
|
"""
|
|
Overriden from superclass.
|
|
|
|
"""
|
|
modem_props = self.GetAll(mm1_constants.I_MODEM)
|
|
m3gpp_props = self.GetAll(mm1_constants.I_MODEM_3GPP)
|
|
retval = {}
|
|
retval['state'] = modem_props['State']
|
|
if retval['state'] >= mm1_constants.MM_MODEM_STATE_REGISTERED:
|
|
retval['signal-quality'] = modem_props['SignalQuality'][0]
|
|
retval['bands'] = modem_props['CurrentBands']
|
|
retval['access-technology'] = self.sim.access_technology
|
|
retval['m3gpp-registration-state'] = \
|
|
m3gpp_props['RegistrationState']
|
|
retval['m3gpp-operator-code'] = m3gpp_props['OperatorCode']
|
|
retval['m3gpp-operator-name'] = m3gpp_props['OperatorName']
|
|
return retval
|
|
# TODO(armansito): implement
|
|
# org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd, if needed
|
|
# (in a separate class?)
|