blob: da932a58165c3e8c45f0408ca706a4b5f0302b20 [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 dbus
import logging
import random
from autotest_lib.client.bin import test
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros.cellular import test_environment
from autotest_lib.client.cros.cellular.pseudomodem import sim
# This is a software only test. Most time delayes are only dbus update delays.
DEFAULT_OPERATION_TIMEOUT=3
class cellular_SIMLocking(test.test):
"""
Test the SIM locking functionality of shill.
This test has the following test_cases:
- Attempt to enable SIM lock with incorrect sim-pin. Verify that the
attempt fails.
- Successfully pin-lock the SIM.
- Unlock a pin-locked SIM.
- Attempt to unlock a pin-locked SIM with incorrect sim-pin, until it gets
puk-locked.
- Unblock a puk-locked SIM.
- Attempt to unblock a puk-locked SIM with incorrect sim-puk, until the
SIM gets blocked. At this point, a sim-pin2 might be expected by some
SIMs. This test does not attempt to unlock the SIM using sim-pin2.
- Test the functionality to change sim-pin.
"""
version = 1
def _bad_pin(self):
""" Obtain a pin that does not match the valid sim-pin. """
# Restricting the values to be >= 1000 ensures four digit string.
bad_pin = random.randint(1000, 9999)
if str(bad_pin) == self.current_pin:
bad_pin += 1
return str(bad_pin)
def _bad_puk(self):
""" Obtain a puk that does not match the valid sim-puk. """
# Restricting the values to be >= 10000000 ensures 8 digit string.
bad_puk = random.randint(10000000, 99999999)
if str(bad_puk) == self.current_puk:
bad_puk += 1
return str(bad_puk)
def _enter_incorrect_pin(self):
try:
self.device.EnterPin(self._bad_pin())
raise error.TestFail('Cellular device did not complain although '
'an incorrect pin was given')
except dbus.DBusException as e:
if e.get_dbus_name() == self.test_env.shill.ERROR_INCORRECT_PIN:
logging.info('Obtained expected result: EnterPin failed with '
'incorrect PIN.')
else:
raise
def _enter_incorrect_puk(self):
try:
self.device.UnblockPin(self._bad_puk(), self.current_pin)
raise error.TestFail('Cellular device did not complain although '
'an incorrect puk was given')
except dbus.DBusException as e:
if e.get_dbus_name() == self.test_env.shill.ERROR_INCORRECT_PIN:
logging.info('Obtained expected result: UnblockPin failed with '
'incorrect PUK.')
else:
raise
def _get_sim_lock_status(self):
""" Helper method to safely obtain SIM lock status. """
properties = self.device.GetProperties(utf8_strings=True)
sim_lock_status = properties.get(
self.test_env.shill.DEVICE_PROPERTY_SIM_LOCK_STATUS,
None)
if sim_lock_status is None:
raise error.TestFail( 'Failed to read SIM_LOCK_STATUS.')
return self.test_env.shill.dbus2primitive(sim_lock_status)
def _is_sim_lock_enabled(self):
""" Helper method to check if the SIM lock is enabled. """
lock_status = self._get_sim_lock_status()
lock_enabled = lock_status.get(
self.test_env.shill.PROPERTY_KEY_SIM_LOCK_ENABLED,
None)
if lock_enabled is None:
raise error.TestFail('Failed to find LockEnabled key in '
'the lock status value.')
return lock_enabled
def _is_sim_pin_locked(self):
""" Helper method to check if the SIM has been pin-locked. """
lock_status = self._get_sim_lock_status()
lock_type = lock_status.get(
self.test_env.shill.PROPERTY_KEY_SIM_LOCK_TYPE,
None)
if lock_type is None:
raise error.TestFail('Failed to find LockType key in the '
'lock status value.')
return lock_type == self.test_env.shill.VALUE_SIM_LOCK_TYPE_PIN
def _is_sim_puk_locked(self):
""" Helper method to check if the SIM has been puk-locked. """
lock_status = self._get_sim_lock_status()
lock_type = lock_status.get(
self.test_env.shill.PROPERTY_KEY_SIM_LOCK_TYPE,
None)
if lock_type is None:
raise error.TestFail('Failed to find LockType key in the '
'lock status value.')
return lock_type == self.test_env.shill.VALUE_SIM_LOCK_TYPE_PUK
def _get_retries_left(self):
""" Helper method to get the number of unlock retries left. """
lock_status = self._get_sim_lock_status()
retries_left = lock_status.get(
self.test_env.shill.PROPERTY_KEY_SIM_LOCK_RETRIES_LEFT,
None)
if retries_left is None:
raise error.TestFail('Failed to find LockRetriesLeft key '
'in the lock status value.')
if retries_left < 0:
raise error.TestFail('Malformed RetriesLeft: %s' %
str(retries_left))
return retries_left
def _reset_modem_with_sim_lock(self):
""" Helper method to reset the modem with the SIM locked. """
# When the SIM is locked, the enable operation fails and
# hence set expect_powered flag to False.
# The enable operation is deferred by Shill until the modem goes into
# the disabled state after the SIM is unlocked.
self.device, self.service = self.test_env.shill.reset_modem(
self.device,
expect_powered=False,
expect_service=False)
def _pin_lock_sim(self):
""" Helper method to pin-lock a SIM, assuming nothing bad happens. """
self.device.RequirePin(self.current_pin, True)
self._reset_modem_with_sim_lock()
if not self._is_sim_pin_locked():
raise error.TestFail('Expected SIM to be locked after reset.')
def _puk_lock_sim(self):
""" Helper method to puk-lock a SIM, assuming nothing bad happens. """
self._pin_lock_sim()
while not self._is_sim_puk_locked():
try:
self._enter_incorrect_pin()
except dbus.DBusException as e:
if e.get_dbus_name() != self.test_env.shill.ERROR_PIN_BLOCKED:
raise
if not self._is_sim_puk_locked():
raise error.TestFail('Expected SIM to be puk-locked.')
def test_unsuccessful_enable_lock(self):
""" Test SIM lock enable failes with incorrect sim-pin. """
logging.debug('Attempting to enable SIM lock with incorrect PIN.')
try:
self.device.RequirePin(self._bad_pin(), True)
raise error.TestFail('Cellular device did not complain although '
'an incorrect pin was given')
except dbus.DBusException as e:
if e.get_dbus_name() == self.test_env.shill.ERROR_INCORRECT_PIN:
logging.info('Obtained expected result: pin-lock enable failed '
'with incorrect PIN.')
else:
raise
if self._is_sim_lock_enabled():
raise error.TestFail('SIM lock got enabled by incorrect PIN.')
# SIM lock should not be enabled, and lock not set after reset.
self.device, self.service = self.test_env.shill.reset_modem(self.device)
self.test_env.shill.wait_for_property_in(self.service,
'state',
['online'],
DEFAULT_OPERATION_TIMEOUT)
if (self._is_sim_lock_enabled() or self._is_sim_pin_locked() or
self._is_sim_puk_locked()):
raise error.TestFail('Cellular device locked by an incorrect pin.')
def test_cause_sim_pin_lock(self):
"""
Test successfully enabling SIM lock and locking the SIM with
pin-lock.
"""
logging.debug('Attempting to enable SIM lock with correct pin.')
self.device.RequirePin(self.current_pin, True)
if not self._is_sim_lock_enabled():
raise error.TestFail('SIM lock was not enabled by correct PIN.')
self._reset_modem_with_sim_lock()
# SIM lock should be enabled, and lock set after reset.
if not self._is_sim_lock_enabled() or not self._is_sim_pin_locked():
raise error.TestFail('Cellular device not locked after reset.')
def test_unlock_sim_pin_lock(self):
"""
Test successfully unlocking the SIM after it has been pin-locked.
"""
# First, pin-lock the SIM.
self._pin_lock_sim()
retries_left = self._get_retries_left()
self.device.EnterPin(self.current_pin)
if self._is_sim_pin_locked():
raise error.TestFail('Failed to unlock a pin-locked SIM with '
'correct pin.')
if not self._is_sim_lock_enabled():
raise error.TestFail('SIM lock got disabled when attemping to'
'unlock a pin-locked SIM.')
if self._get_retries_left() != retries_left:
raise error.TestFail('Unexpected change in number of retries left '
'after a successful unlock of pin-locked SIM. '
'retries before:%d, after:%d' %
(retries_left, self._get_retries_left()))
# The shill service reappears after the SIM is unlocked.
# We need a fresh handle on the service.
utils.poll_for_condition(
lambda: self.test_env.shill.get_service_for_device(self.device))
self.service = self.test_env.shill.get_service_for_device(self.device)
self.test_env.shill.wait_for_property_in(self.service,
'state',
['online'],
DEFAULT_OPERATION_TIMEOUT)
def test_cause_sim_puk_lock(self):
""" Test the flow that causes a SIM to be puk-locked. """
# First, pin-lock the SIM.
self._pin_lock_sim()
# Expire all unlock pin-lock retries.
retries_left = self._get_retries_left()
if retries_left <= 0:
raise error.TestFail('Expected a positive number of sim-puk '
'retries.')
while self._get_retries_left() > 1:
# Don't execute the loop down to 0, as retries_left may be reset to
# a higher value corresponding to the puk-lock retries.
self._enter_incorrect_pin()
if retries_left - self._get_retries_left() != 1:
raise error.TestFail('RetriesLeft not decremented correctly by '
'an attempt to unlock pin-lock with bad '
'PIN.')
retries_left = self._get_retries_left()
# retries_left == 1
try:
self._enter_incorrect_pin()
raise error.TestFail('Shill failed to throw PinBlocked error.')
except dbus.DBusException as e:
if e.get_dbus_name() != self.test_env.shill.ERROR_PIN_BLOCKED:
raise
# At this point, the SIM should be puk-locked.
if not self._is_sim_lock_enabled() or not self._is_sim_puk_locked():
raise error.TestFail('Could not puk-lock the SIM after sufficient '
'incorrect attempts to unlock.')
if not self._get_retries_left():
raise error.TestFail('RetriesLeft not updated to puk-lock retries '
'after the SIM got puk-locked.')
def test_unlock_sim_puk_lock(self):
""" Unlock a puk-locked SIM. """
# First, puk-lock the SIM
self._puk_lock_sim()
retries_left = self._get_retries_left()
self.device.UnblockPin(self.current_puk, self.current_pin)
if self._is_sim_puk_locked():
raise error.TestFail('Failed to unlock a puk-locked SIM with '
'correct puk.')
if self._is_sim_pin_locked():
raise error.TestFail('pin-lock got unlocked while unlocking the '
'puk-lock.')
if not self._is_sim_lock_enabled():
raise error.TestFail('SIM lock got disabled when attemping to'
'unlock a pin-locked SIM.')
def test_brick_sim(self):
""" Test the flow that expires all pin-lock and puk-lock retries. """
# First, puk-lock the SIM.
self._puk_lock_sim()
# Expire all unlock puk-lock retries.
retries_left = self._get_retries_left()
if retries_left <= 0:
raise error.TestFail('Expected a positive number of sim-puk '
'retries.')
while self._get_retries_left() > 1:
# Don't execute the loop down to 0, as the exception raised on the
# last attempt is different.
self._enter_incorrect_puk()
if retries_left - self._get_retries_left() != 1:
raise error.TestFail('RetriesLeft not decremented correctly by '
'an attempt to unlock puk-lock with bad '
'PUK.')
retries_left = self._get_retries_left()
# retries_left == 1
try:
self._enter_incorrect_puk()
raise error.TestFail('Shill failed to throw SimFailure error.')
except dbus.DBusException as e:
if e.get_dbus_name() != self.test_env.shill.ERROR_FAILURE:
raise
def test_change_pin(self):
""" Test changing pin successfully and unsuccessfully. """
# The currently accepted behaviour of ChangePin is -- it succeeds if
# (1) SIM locking is enabled.
# (2) SIM is currently not locked.
# (3) The correct sim-pin is used as the old_pin argument in ChangePin.
# ChangePin will fail in all other conditions. It sometimes fails
# obviously, with an error. In other cases, it silently fails to change
# the sim-pin.
new_pin = self._bad_pin()
# Attempt to change the sim-pin when SIM locking is not enabled.
try:
self.device.ChangePin(self.current_pin, new_pin)
raise error.TestFail('Expected ChangePin to fail when SIM lock is '
'not enabled.')
except dbus.DBusException as e:
if e.get_dbus_name() != self.test_env.shill.ERROR_FAILURE:
raise
self.device.RequirePin(self.current_pin, True)
# Attempt to change the sim-pin with incorrect current sim_pin.
try:
self.device.ChangePin(self._bad_pin(), new_pin)
raise error.TestFail('Expected ChangePin to fail with incorrect '
'sim-pin.')
except dbus.DBusException as e:
if e.get_dbus_name() != self.test_env.shill.ERROR_INCORRECT_PIN:
raise
# Change sim-pin successfully.
self.device.ChangePin(self.current_pin, new_pin)
self.current_pin = new_pin
self.device.RequirePin(self.current_pin, False)
if self._is_sim_lock_enabled():
raise error.TestFail('Expected to be able to disable SIM lock with '
'the new sim-pin')
def _run_internal(self, test_to_run):
"""
Entry point to run all tests.
@param test_to_run is a function that runs the required test.
"""
self.current_pin = sim.SIM.DEFAULT_PIN
self.current_puk = sim.SIM.DEFAULT_PUK
# Resetting modemmanager invalidates the shill dbus object for the
# modem.
self.device = self.test_env.shill.find_cellular_device_object()
if not self.device:
raise error.TestFail('Failed to find a cellular device.')
# Be a little cynical and make sure that SIM locks are as expected
# before we begin.
if (self._is_sim_lock_enabled() or self._is_sim_pin_locked() or
self._is_sim_puk_locked()):
raise error.TestFail(
'Cellular device in bad initial sim-lock state. '
'LockEnabled: %b, PinLocked:%b, PukLocked:%b.' %
(self._is_sim_lock_enabled(), self._is_sim_pin_locked(),
self._is_sim_puk_locked()))
test_to_run()
def run_once(self):
"""Entry function into the test."""
random.seed()
test_list = [self.test_unsuccessful_enable_lock,
self.test_cause_sim_pin_lock,
self.test_unlock_sim_pin_lock,
self.test_cause_sim_puk_lock,
self.test_unlock_sim_puk_lock,
self.test_brick_sim,
self.test_change_pin]
# Some of these tests render the modem unusable, so run each test
# with a fresh pseudomodem.
for test in test_list:
self.test_env = test_environment.CellularPseudoMMTestEnvironment(
pseudomm_args=({'family': '3GPP'},))
with self.test_env:
self._run_internal(test)