blob: aef162fa6ce10b48df8296649037a2402f38fb81 [file] [log] [blame]
# Copyright (c) 2013 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 math
# Setup wardmodem package root and other autotest paths.
import common
import state_machine
class NetworkIdentityMachine(state_machine.StateMachine):
"""
Various identification numbers are used by the network to identify a
customer's mobile equipment. These numbers are variously dependent on the
SIM card, the network, the original subscriber, the geographical location
etc.
We serve queries regarding these identification numbers from this state
machine, so that intuitive handles can be used to fake different
combinations of the facets listed above.
"""
def __init__(self, state, transceiver, modem_conf):
"""
@param state: The GlobalState object shared by all state machines.
@param transceiver: The ATTransceiver object to interact with.
@param modem_conf: A ModemConfiguration object containing the
configuration data for the current modem.
"""
super(NetworkIdentityMachine, self).__init__(state, transceiver,
modem_conf)
# Register all responses.
self._add_response_function('wm_response_sim_info_success')
self._add_response_function('wm_response_sim_info_error_too_long')
self._add_response_function('wm_response_mdn')
# Load configuration.
self._mcc = modem_conf.network_identity_default_mcc
self._mnc = modem_conf.network_identity_default_mnc
self._msin = modem_conf.network_identity_default_msin
self._mdn = modem_conf.network_identity_default_mdn
def get_well_known_name(self):
""" Returns the well known name for this machine. """
return 'network_identity_machine'
# ##########################################################################
# State machine API functions.
def read_imsi_from_modem(self):
"""
Return the IMSI stored in the modem.
This is currently a stub that returns the same IMSI as returned from
SIM (@see read_sim_imsi). Note that the format of the returned value is
different for these two functions. Some modems actually report two
different IMSIs depending on where it is queried from. Implement this
function if you want to simulate that behaviour.
"""
self._respond_text(self._get_sim_imsi())
self._respond_ok()
def read_sim_admin_data(self, length_str):
"""
Return administrative data read from the SIM.
The administrative data contains, besides the length of the MNC, the
state of the device -- is it under active development, undergoing some
qualification process etc.
We force the mode to be normal, and allow modems to provide different
length of MNC if desired.
@param length_str: (Type: str) Length of expected response.
"""
answer = '0000000'
answer += str(len(self._mnc))
self._check_length_and_respond(answer, length_str)
def read_sim_imsi(self, length_str):
"""
Return the IMSI.
The IMSI information is also available from request_response state
machine. These two IMSI values can actually be different.
@param length_str: (Type: str) Length of the expected response.
"""
imsi = self._get_sim_imsi()
# See ETSI TS 151.11 V14.4.0
# The format of the returned string is
# 'l' ['x' '<padded_imsi>']
#
# |l| is the number of bytes used by the 'x' and 'imsi' together.
#
# |x| encodes some parity checking, and can be '8' or '9' based on
# parity. We currently set it to 9 (I haven't seen '8' in practice')
#
# |padded_imsi| is imsi padded with 'f' on the right to make the whole
# thing 18 characters
#
# Finally the bytes within [] are represented LSB first, so the odd and
# even characters in that string need to be swapped.
x = '9'
padded_imsi = x + imsi
l = str(int(math.ceil(len(padded_imsi)/2)))
while len(padded_imsi) < 16:
padded_imsi += 'F'
# Encode this number LSB first.
switched_imsi = []
for i in range(8):
switched_imsi.append(padded_imsi[2*i+1])
switched_imsi.append(padded_imsi[2*i])
padded_imsi = ''.join(switched_imsi)
# l should be 2 character long
if len(l) != 2:
l = '0' + l
response = l + padded_imsi
self._check_length_and_respond(response, length_str)
def read_service_provider_name(self, length_str):
"""
Return the name of the service provider encoded as hex string.
Not Implemented. None of the modems we use return this information
correctly right now.
@param length_str: (Type: str) Length of the expected response.
"""
# 'F' is used to pad unused bits. An all-'F' string means that we do not
# have this information.
# (TODO) pprabhu: Figure out the encoding scheme for this information if
# a test needs it.
provider_name = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
self._check_length_and_respond(provider_name, length_str)
def read_mdn(self):
"""
Return the Mobile Directory Number for the current service account.
"""
self._respond(self.wm_response_mdn, 0, self._mdn)
self._respond_ok()
def _check_length_and_respond(self, response, length_str):
"""
Checks that the length of the response is not less than the expected
response. If it is more, the response is clipped.
@param response: The string to respond with.
@param length_str: (Type: str)The expected length in bytes. Note that
the number of bytes in |response| is half the length of the
string |response|, since each byte encodes two characters.
"""
try:
length = int(length_str)
except ValueError as e:
dbgstr = self._tag_with_name(
'Failed to detect expected length of the response. '
'Are you sure this is a number: |%s|' % length_str)
self._respond_error()
return
# We require ceil(len(response)) number of bytes to encode the string
# |response|, since each byte holds two characters.
if 2 * length > len(response):
dbgstr = self._tag_with_name(
'Response too short. Requested reponse length: %d, Actual '
'response length: %d' % (2 * length, len(response)))
self._logger.warning(dbgstr)
self._respond(self.wm_response_sim_info_error_too_long)
else:
dbgstr = self._tag_with_name(
'Response: |%s|, clipped to length %d: |%s|' %
(response, length, response[:2*length]))
self._logger.debug(dbgstr)
self._respond(self.wm_response_sim_info_success, 0,
response[:2*length])
self._respond_ok()
def _get_sim_imsi(self):
return self._mcc + self._mnc + self._msin