• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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