1 # Copyright (c) 2013 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 math 6 7 # Setup wardmodem package root and other autotest paths. 8 import common 9 10 import state_machine 11 12 class NetworkIdentityMachine(state_machine.StateMachine): 13 """ 14 Various identification numbers are used by the network to identify a 15 customer's mobile equipment. These numbers are variously dependent on the 16 SIM card, the network, the original subscriber, the geographical location 17 etc. 18 19 We serve queries regarding these identification numbers from this state 20 machine, so that intuitive handles can be used to fake different 21 combinations of the facets listed above. 22 23 """ 24 25 def __init__(self, state, transceiver, modem_conf): 26 """ 27 @param state: The GlobalState object shared by all state machines. 28 29 @param transceiver: The ATTransceiver object to interact with. 30 31 @param modem_conf: A ModemConfiguration object containing the 32 configuration data for the current modem. 33 34 """ 35 super(NetworkIdentityMachine, self).__init__(state, transceiver, 36 modem_conf) 37 38 # Register all responses. 39 self._add_response_function('wm_response_sim_info_success') 40 self._add_response_function('wm_response_sim_info_error_too_long') 41 self._add_response_function('wm_response_mdn') 42 43 # Load configuration. 44 self._mcc = modem_conf.network_identity_default_mcc 45 self._mnc = modem_conf.network_identity_default_mnc 46 self._msin = modem_conf.network_identity_default_msin 47 self._mdn = modem_conf.network_identity_default_mdn 48 49 50 def get_well_known_name(self): 51 """ Returns the well known name for this machine. """ 52 return 'network_identity_machine' 53 54 55 # ########################################################################## 56 # State machine API functions. 57 def read_imsi_from_modem(self): 58 """ 59 Return the IMSI stored in the modem. 60 61 This is currently a stub that returns the same IMSI as returned from 62 SIM (@see read_sim_imsi). Note that the format of the returned value is 63 different for these two functions. Some modems actually report two 64 different IMSIs depending on where it is queried from. Implement this 65 function if you want to simulate that behaviour. 66 67 """ 68 self._respond_text(self._get_sim_imsi()) 69 self._respond_ok() 70 71 72 def read_sim_admin_data(self, length_str): 73 """ 74 Return administrative data read from the SIM. 75 76 The administrative data contains, besides the length of the MNC, the 77 state of the device -- is it under active development, undergoing some 78 qualification process etc. 79 We force the mode to be normal, and allow modems to provide different 80 length of MNC if desired. 81 82 @param length_str: (Type: str) Length of expected response. 83 84 """ 85 answer = '0000000' 86 answer += str(len(self._mnc)) 87 self._check_length_and_respond(answer, length_str) 88 89 90 def read_sim_imsi(self, length_str): 91 """ 92 Return the IMSI. 93 94 The IMSI information is also available from request_response state 95 machine. These two IMSI values can actually be different. 96 97 @param length_str: (Type: str) Length of the expected response. 98 99 """ 100 imsi = self._get_sim_imsi() 101 # See ETSI TS 151.11 V14.4.0 102 # The format of the returned string is 103 # 'l' ['x' '<padded_imsi>'] 104 # 105 # |l| is the number of bytes used by the 'x' and 'imsi' together. 106 # 107 # |x| encodes some parity checking, and can be '8' or '9' based on 108 # parity. We currently set it to 9 (I haven't seen '8' in practice') 109 # 110 # |padded_imsi| is imsi padded with 'f' on the right to make the whole 111 # thing 18 characters 112 # 113 # Finally the bytes within [] are represented LSB first, so the odd and 114 # even characters in that string need to be swapped. 115 x = '9' 116 padded_imsi = x + imsi 117 l = str(int(math.ceil(len(padded_imsi)/2))) 118 while len(padded_imsi) < 16: 119 padded_imsi += 'F' 120 # Encode this number LSB first. 121 switched_imsi = [] 122 for i in range(8): 123 switched_imsi.append(padded_imsi[2*i+1]) 124 switched_imsi.append(padded_imsi[2*i]) 125 padded_imsi = ''.join(switched_imsi) 126 127 # l should be 2 character long 128 if len(l) != 2: 129 l = '0' + l 130 response = l + padded_imsi 131 132 self._check_length_and_respond(response, length_str) 133 134 135 def read_service_provider_name(self, length_str): 136 """ 137 Return the name of the service provider encoded as hex string. 138 139 Not Implemented. None of the modems we use return this information 140 correctly right now. 141 142 @param length_str: (Type: str) Length of the expected response. 143 144 """ 145 # 'F' is used to pad unused bits. An all-'F' string means that we do not 146 # have this information. 147 # (TODO) pprabhu: Figure out the encoding scheme for this information if 148 # a test needs it. 149 provider_name = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' 150 self._check_length_and_respond(provider_name, length_str) 151 152 153 def read_mdn(self): 154 """ 155 Return the Mobile Directory Number for the current service account. 156 157 """ 158 self._respond(self.wm_response_mdn, 0, self._mdn) 159 self._respond_ok() 160 161 162 def _check_length_and_respond(self, response, length_str): 163 """ 164 Checks that the length of the response is not less than the expected 165 response. If it is more, the response is clipped. 166 167 @param response: The string to respond with. 168 169 @param length_str: (Type: str)The expected length in bytes. Note that 170 the number of bytes in |response| is half the length of the 171 string |response|, since each byte encodes two characters. 172 173 """ 174 try: 175 length = int(length_str) 176 except ValueError as e: 177 dbgstr = self._tag_with_name( 178 'Failed to detect expected length of the response. ' 179 'Are you sure this is a number: |%s|' % length_str) 180 self._respond_error() 181 return 182 183 # We require ceil(len(response)) number of bytes to encode the string 184 # |response|, since each byte holds two characters. 185 if 2 * length > len(response): 186 dbgstr = self._tag_with_name( 187 'Response too short. Requested reponse length: %d, Actual ' 188 'response length: %d' % (2 * length, len(response))) 189 self._logger.warning(dbgstr) 190 self._respond(self.wm_response_sim_info_error_too_long) 191 else: 192 dbgstr = self._tag_with_name( 193 'Response: |%s|, clipped to length %d: |%s|' % 194 (response, length, response[:2*length])) 195 self._logger.debug(dbgstr) 196 self._respond(self.wm_response_sim_info_success, 0, 197 response[:2*length]) 198 self._respond_ok() 199 200 201 def _get_sim_imsi(self): 202 return self._mcc + self._mnc + self._msin 203