Home | History | Annotate | Download | only in network_SIMLocking
      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 dbus
      6 import logging
      7 import random
      8 
      9 from autotest_lib.client.bin import test
     10 from autotest_lib.client.bin import utils
     11 from autotest_lib.client.common_lib import error
     12 from autotest_lib.client.cros.cellular import test_environment
     13 from autotest_lib.client.cros.cellular.pseudomodem import sim
     14 
     15 # This is a software only test. Most time delayes are only dbus update delays.
     16 DEFAULT_OPERATION_TIMEOUT=3
     17 
     18 class network_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