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 5import dbus 6import logging 7import random 8 9from autotest_lib.client.bin import test 10from autotest_lib.client.bin import utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.cros.cellular import test_environment 13from autotest_lib.client.cros.cellular.pseudomodem import sim 14 15# This is a software only test. Most time delayes are only dbus update delays. 16DEFAULT_OPERATION_TIMEOUT=3 17 18class cellular_SIMLocking(test.test): 19 """ 20 Test the SIM locking functionality of shill. 21 22 This test has the following test_cases: 23 - Attempt to enable SIM lock with incorrect sim-pin. Verify that the 24 attempt fails. 25 - Successfully pin-lock the SIM. 26 - Unlock a pin-locked SIM. 27 - Attempt to unlock a pin-locked SIM with incorrect sim-pin, until it gets 28 puk-locked. 29 - Unblock a puk-locked SIM. 30 - Attempt to unblock a puk-locked SIM with incorrect sim-puk, until the 31 SIM gets blocked. At this point, a sim-pin2 might be expected by some 32 SIMs. This test does not attempt to unlock the SIM using sim-pin2. 33 - Test the functionality to change sim-pin. 34 35 """ 36 37 version = 1 38 39 def _bad_pin(self): 40 """ Obtain a pin that does not match the valid sim-pin. """ 41 # Restricting the values to be >= 1000 ensures four digit string. 42 bad_pin = random.randint(1000, 9999) 43 if str(bad_pin) == self.current_pin: 44 bad_pin += 1 45 return str(bad_pin) 46 47 48 def _bad_puk(self): 49 """ Obtain a puk that does not match the valid sim-puk. """ 50 # Restricting the values to be >= 10000000 ensures 8 digit string. 51 bad_puk = random.randint(10000000, 99999999) 52 if str(bad_puk) == self.current_puk: 53 bad_puk += 1 54 return str(bad_puk) 55 56 57 def _enter_incorrect_pin(self): 58 try: 59 self.device.EnterPin(self._bad_pin()) 60 raise error.TestFail('Cellular device did not complain although ' 61 'an incorrect pin was given') 62 except dbus.DBusException as e: 63 if e.get_dbus_name() == self.test_env.shill.ERROR_INCORRECT_PIN: 64 logging.info('Obtained expected result: EnterPin failed with ' 65 'incorrect PIN.') 66 else: 67 raise 68 69 70 def _enter_incorrect_puk(self): 71 try: 72 self.device.UnblockPin(self._bad_puk(), self.current_pin) 73 raise error.TestFail('Cellular device did not complain although ' 74 'an incorrect puk was given') 75 except dbus.DBusException as e: 76 if e.get_dbus_name() == self.test_env.shill.ERROR_INCORRECT_PIN: 77 logging.info('Obtained expected result: UnblockPin failed with ' 78 'incorrect PUK.') 79 else: 80 raise 81 82 83 def _get_sim_lock_status(self): 84 """ Helper method to safely obtain SIM lock status. """ 85 properties = self.device.GetProperties(utf8_strings=True) 86 sim_lock_status = properties.get( 87 self.test_env.shill.DEVICE_PROPERTY_SIM_LOCK_STATUS, 88 None) 89 if sim_lock_status is None: 90 raise error.TestFail( 'Failed to read SIM_LOCK_STATUS.') 91 return self.test_env.shill.dbus2primitive(sim_lock_status) 92 93 94 def _is_sim_lock_enabled(self): 95 """ Helper method to check if the SIM lock is enabled. """ 96 lock_status = self._get_sim_lock_status() 97 lock_enabled = lock_status.get( 98 self.test_env.shill.PROPERTY_KEY_SIM_LOCK_ENABLED, 99 None) 100 if lock_enabled is None: 101 raise error.TestFail('Failed to find LockEnabled key in ' 102 'the lock status value.') 103 return lock_enabled 104 105 106 def _is_sim_pin_locked(self): 107 """ Helper method to check if the SIM has been pin-locked. """ 108 lock_status = self._get_sim_lock_status() 109 lock_type = lock_status.get( 110 self.test_env.shill.PROPERTY_KEY_SIM_LOCK_TYPE, 111 None) 112 if lock_type is None: 113 raise error.TestFail('Failed to find LockType key in the ' 114 'lock status value.') 115 return lock_type == self.test_env.shill.VALUE_SIM_LOCK_TYPE_PIN 116 117 118 def _is_sim_puk_locked(self): 119 """ Helper method to check if the SIM has been puk-locked. """ 120 lock_status = self._get_sim_lock_status() 121 lock_type = lock_status.get( 122 self.test_env.shill.PROPERTY_KEY_SIM_LOCK_TYPE, 123 None) 124 if lock_type is None: 125 raise error.TestFail('Failed to find LockType key in the ' 126 'lock status value.') 127 return lock_type == self.test_env.shill.VALUE_SIM_LOCK_TYPE_PUK 128 129 130 def _get_retries_left(self): 131 """ Helper method to get the number of unlock retries left. """ 132 lock_status = self._get_sim_lock_status() 133 retries_left = lock_status.get( 134 self.test_env.shill.PROPERTY_KEY_SIM_LOCK_RETRIES_LEFT, 135 None) 136 if retries_left is None: 137 raise error.TestFail('Failed to find LockRetriesLeft key ' 138 'in the lock status value.') 139 if retries_left < 0: 140 raise error.TestFail('Malformed RetriesLeft: %s' % 141 str(retries_left)) 142 return retries_left 143 144 def _reset_modem_with_sim_lock(self): 145 """ Helper method to reset the modem with the SIM locked. """ 146 # When the SIM is locked, the enable operation fails and 147 # hence set expect_powered flag to False. 148 # The enable operation is deferred by Shill until the modem goes into 149 # the disabled state after the SIM is unlocked. 150 self.device, self.service = self.test_env.shill.reset_modem( 151 self.device, 152 expect_powered=False, 153 expect_service=False) 154 155 def _pin_lock_sim(self): 156 """ Helper method to pin-lock a SIM, assuming nothing bad happens. """ 157 self.device.RequirePin(self.current_pin, True) 158 self._reset_modem_with_sim_lock() 159 if not self._is_sim_pin_locked(): 160 raise error.TestFail('Expected SIM to be locked after reset.') 161 162 163 def _puk_lock_sim(self): 164 """ Helper method to puk-lock a SIM, assuming nothing bad happens. """ 165 self._pin_lock_sim() 166 while not self._is_sim_puk_locked(): 167 try: 168 self._enter_incorrect_pin() 169 except dbus.DBusException as e: 170 if e.get_dbus_name() != self.test_env.shill.ERROR_PIN_BLOCKED: 171 raise 172 if not self._is_sim_puk_locked(): 173 raise error.TestFail('Expected SIM to be puk-locked.') 174 175 176 177 178 def test_unsuccessful_enable_lock(self): 179 """ Test SIM lock enable failes with incorrect sim-pin. """ 180 logging.debug('Attempting to enable SIM lock with incorrect PIN.') 181 try: 182 self.device.RequirePin(self._bad_pin(), True) 183 raise error.TestFail('Cellular device did not complain although ' 184 'an incorrect pin was given') 185 except dbus.DBusException as e: 186 if e.get_dbus_name() == self.test_env.shill.ERROR_INCORRECT_PIN: 187 logging.info('Obtained expected result: pin-lock enable failed ' 188 'with incorrect PIN.') 189 else: 190 raise 191 192 if self._is_sim_lock_enabled(): 193 raise error.TestFail('SIM lock got enabled by incorrect PIN.') 194 195 # SIM lock should not be enabled, and lock not set after reset. 196 self.device, self.service = self.test_env.shill.reset_modem(self.device) 197 self.test_env.shill.wait_for_property_in(self.service, 198 'state', 199 ['online'], 200 DEFAULT_OPERATION_TIMEOUT) 201 if (self._is_sim_lock_enabled() or self._is_sim_pin_locked() or 202 self._is_sim_puk_locked()): 203 raise error.TestFail('Cellular device locked by an incorrect pin.') 204 205 206 def test_cause_sim_pin_lock(self): 207 """ 208 Test successfully enabling SIM lock and locking the SIM with 209 pin-lock. 210 211 """ 212 logging.debug('Attempting to enable SIM lock with correct pin.') 213 self.device.RequirePin(self.current_pin, True) 214 215 if not self._is_sim_lock_enabled(): 216 raise error.TestFail('SIM lock was not enabled by correct PIN.') 217 218 self._reset_modem_with_sim_lock() 219 # SIM lock should be enabled, and lock set after reset. 220 if not self._is_sim_lock_enabled() or not self._is_sim_pin_locked(): 221 raise error.TestFail('Cellular device not locked after reset.') 222 223 224 def test_unlock_sim_pin_lock(self): 225 """ 226 Test successfully unlocking the SIM after it has been pin-locked. 227 228 """ 229 # First, pin-lock the SIM. 230 self._pin_lock_sim() 231 232 retries_left = self._get_retries_left() 233 self.device.EnterPin(self.current_pin) 234 235 if self._is_sim_pin_locked(): 236 raise error.TestFail('Failed to unlock a pin-locked SIM with ' 237 'correct pin.') 238 if not self._is_sim_lock_enabled(): 239 raise error.TestFail('SIM lock got disabled when attemping to' 240 'unlock a pin-locked SIM.') 241 if self._get_retries_left() != retries_left: 242 raise error.TestFail('Unexpected change in number of retries left ' 243 'after a successful unlock of pin-locked SIM. ' 244 'retries before:%d, after:%d' % 245 (retries_left, self._get_retries_left())) 246 # The shill service reappears after the SIM is unlocked. 247 # We need a fresh handle on the service. 248 utils.poll_for_condition( 249 lambda: self.test_env.shill.get_service_for_device(self.device)) 250 self.service = self.test_env.shill.get_service_for_device(self.device) 251 self.test_env.shill.wait_for_property_in(self.service, 252 'state', 253 ['online'], 254 DEFAULT_OPERATION_TIMEOUT) 255 256 257 def test_cause_sim_puk_lock(self): 258 """ Test the flow that causes a SIM to be puk-locked. """ 259 # First, pin-lock the SIM. 260 self._pin_lock_sim() 261 262 # Expire all unlock pin-lock retries. 263 retries_left = self._get_retries_left() 264 if retries_left <= 0: 265 raise error.TestFail('Expected a positive number of sim-puk ' 266 'retries.') 267 268 while self._get_retries_left() > 1: 269 # Don't execute the loop down to 0, as retries_left may be reset to 270 # a higher value corresponding to the puk-lock retries. 271 self._enter_incorrect_pin() 272 if retries_left - self._get_retries_left() != 1: 273 raise error.TestFail('RetriesLeft not decremented correctly by ' 274 'an attempt to unlock pin-lock with bad ' 275 'PIN.') 276 retries_left = self._get_retries_left() 277 278 # retries_left == 1 279 try: 280 self._enter_incorrect_pin() 281 raise error.TestFail('Shill failed to throw PinBlocked error.') 282 except dbus.DBusException as e: 283 if e.get_dbus_name() != self.test_env.shill.ERROR_PIN_BLOCKED: 284 raise 285 286 # At this point, the SIM should be puk-locked. 287 if not self._is_sim_lock_enabled() or not self._is_sim_puk_locked(): 288 raise error.TestFail('Could not puk-lock the SIM after sufficient ' 289 'incorrect attempts to unlock.') 290 if not self._get_retries_left(): 291 raise error.TestFail('RetriesLeft not updated to puk-lock retries ' 292 'after the SIM got puk-locked.') 293 294 295 def test_unlock_sim_puk_lock(self): 296 """ Unlock a puk-locked SIM. """ 297 # First, puk-lock the SIM 298 self._puk_lock_sim() 299 300 retries_left = self._get_retries_left() 301 self.device.UnblockPin(self.current_puk, self.current_pin) 302 303 if self._is_sim_puk_locked(): 304 raise error.TestFail('Failed to unlock a puk-locked SIM with ' 305 'correct puk.') 306 if self._is_sim_pin_locked(): 307 raise error.TestFail('pin-lock got unlocked while unlocking the ' 308 'puk-lock.') 309 if not self._is_sim_lock_enabled(): 310 raise error.TestFail('SIM lock got disabled when attemping to' 311 'unlock a pin-locked SIM.') 312 313 def test_brick_sim(self): 314 """ Test the flow that expires all pin-lock and puk-lock retries. """ 315 # First, puk-lock the SIM. 316 self._puk_lock_sim() 317 318 # Expire all unlock puk-lock retries. 319 retries_left = self._get_retries_left() 320 if retries_left <= 0: 321 raise error.TestFail('Expected a positive number of sim-puk ' 322 'retries.') 323 324 while self._get_retries_left() > 1: 325 # Don't execute the loop down to 0, as the exception raised on the 326 # last attempt is different. 327 self._enter_incorrect_puk() 328 if retries_left - self._get_retries_left() != 1: 329 raise error.TestFail('RetriesLeft not decremented correctly by ' 330 'an attempt to unlock puk-lock with bad ' 331 'PUK.') 332 retries_left = self._get_retries_left() 333 334 # retries_left == 1 335 try: 336 self._enter_incorrect_puk() 337 raise error.TestFail('Shill failed to throw SimFailure error.') 338 except dbus.DBusException as e: 339 if e.get_dbus_name() != self.test_env.shill.ERROR_FAILURE: 340 raise 341 342 343 def test_change_pin(self): 344 """ Test changing pin successfully and unsuccessfully. """ 345 # The currently accepted behaviour of ChangePin is -- it succeeds if 346 # (1) SIM locking is enabled. 347 # (2) SIM is currently not locked. 348 # (3) The correct sim-pin is used as the old_pin argument in ChangePin. 349 # ChangePin will fail in all other conditions. It sometimes fails 350 # obviously, with an error. In other cases, it silently fails to change 351 # the sim-pin. 352 new_pin = self._bad_pin() 353 # Attempt to change the sim-pin when SIM locking is not enabled. 354 try: 355 self.device.ChangePin(self.current_pin, new_pin) 356 raise error.TestFail('Expected ChangePin to fail when SIM lock is ' 357 'not enabled.') 358 except dbus.DBusException as e: 359 if e.get_dbus_name() != self.test_env.shill.ERROR_FAILURE: 360 raise 361 362 self.device.RequirePin(self.current_pin, True) 363 # Attempt to change the sim-pin with incorrect current sim_pin. 364 try: 365 self.device.ChangePin(self._bad_pin(), new_pin) 366 raise error.TestFail('Expected ChangePin to fail with incorrect ' 367 'sim-pin.') 368 except dbus.DBusException as e: 369 if e.get_dbus_name() != self.test_env.shill.ERROR_INCORRECT_PIN: 370 raise 371 372 # Change sim-pin successfully. 373 self.device.ChangePin(self.current_pin, new_pin) 374 self.current_pin = new_pin 375 self.device.RequirePin(self.current_pin, False) 376 if self._is_sim_lock_enabled(): 377 raise error.TestFail('Expected to be able to disable SIM lock with ' 378 'the new sim-pin') 379 380 381 def _run_internal(self, test_to_run): 382 """ 383 Entry point to run all tests. 384 385 @param test_to_run is a function that runs the required test. 386 387 """ 388 self.current_pin = sim.SIM.DEFAULT_PIN 389 self.current_puk = sim.SIM.DEFAULT_PUK 390 391 # Resetting modemmanager invalidates the shill dbus object for the 392 # modem. 393 self.device = self.test_env.shill.find_cellular_device_object() 394 if not self.device: 395 raise error.TestFail('Failed to find a cellular device.') 396 397 # Be a little cynical and make sure that SIM locks are as expected 398 # before we begin. 399 if (self._is_sim_lock_enabled() or self._is_sim_pin_locked() or 400 self._is_sim_puk_locked()): 401 raise error.TestFail( 402 'Cellular device in bad initial sim-lock state. ' 403 'LockEnabled: %b, PinLocked:%b, PukLocked:%b.' % 404 (self._is_sim_lock_enabled(), self._is_sim_pin_locked(), 405 self._is_sim_puk_locked())) 406 407 test_to_run() 408 409 410 def run_once(self): 411 """Entry function into the test.""" 412 random.seed() 413 test_list = [self.test_unsuccessful_enable_lock, 414 self.test_cause_sim_pin_lock, 415 self.test_unlock_sim_pin_lock, 416 self.test_cause_sim_puk_lock, 417 self.test_unlock_sim_puk_lock, 418 self.test_brick_sim, 419 self.test_change_pin] 420 421 # Some of these tests render the modem unusable, so run each test 422 # with a fresh pseudomodem. 423 for test in test_list: 424 self.test_env = test_environment.CellularPseudoMMTestEnvironment( 425 pseudomm_args=({'family': '3GPP'},)) 426 with self.test_env: 427 self._run_internal(test) 428