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