• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#  Copyright (C) 2024 The Android Open Source Project
2#
3#  Licensed under the Apache License, Version 2.0 (the "License");
4#  you may not use this file except in compliance with the License.
5#  You may obtain a copy of the License at
6#
7#       http://www.apache.org/licenses/LICENSE-2.0
8#
9#  Unless required by applicable law or agreed to in writing, software
10#  distributed under the License is distributed on an "AS IS" BASIS,
11#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12#  See the License for the specific language governing permissions and
13#  limitations under the License.
14#
15#  Licensed under the Apache License, Version 2.0 (the "License");
16#  you may not use this file except in compliance with the License.
17#  You may obtain a copy of the License at
18#
19#       http://www.apache.org/licenses/LICENSE-2.0
20#
21#  Unless required by applicable law or agreed to in writing, software
22#  distributed under the License is distributed on an "AS IS" BASIS,
23#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24#  See the License for the specific language governing permissions and
25#  limitations under the License.
26
27# Lint as: python3
28"""CTS Tests that verify NFC HCE features.
29
30These tests require one phone and one PN532 board. The phone acts as a card emulator, and the PN532
31acts as an NFC reader. The devices should be placed back to back.
32"""
33
34from http.client import HTTPSConnection
35import json
36import logging
37import ssl
38import sys
39import time
40
41from android.platform.test.annotations import CddTest
42from android.platform.test.annotations import ApiTest
43from mobly import asserts
44from mobly import base_test
45from mobly import test_runner
46from mobly import utils
47from mobly.controllers import android_device
48from mobly.controllers.android_device_lib import adb
49
50
51_LOG = logging.getLogger(__name__)
52logging.basicConfig(level=logging.INFO)
53try:
54    import pn532
55    from pn532.nfcutils import (
56        parse_protocol_params,
57        create_select_apdu,
58        poll_and_transact,
59        poll_and_observe_frames,
60        get_apdus,
61        POLLING_FRAME_ALL_TEST_CASES,
62        POLLING_FRAMES_TYPE_A_SPECIAL,
63        POLLING_FRAMES_TYPE_B_SPECIAL,
64        POLLING_FRAMES_TYPE_F_SPECIAL,
65        POLLING_FRAMES_TYPE_A_LONG,
66        POLLING_FRAMES_TYPE_B_LONG,
67        POLLING_FRAME_ON,
68        POLLING_FRAME_OFF,
69        get_frame_test_stats,
70        TimedWrapper,
71        ns_to_ms,
72        ns_to_us,
73        us_to_ms,
74    )
75except ImportError as e:
76    _LOG.warning(f"Cannot import PN532 library due to {e}")
77
78# Timeout to give the NFC service time to perform async actions such as
79# discover tags.
80_NFC_TIMEOUT_SEC = 10
81_NFC_TECH_A_POLLING_ON = (0x1 #NfcAdapter.FLAG_READER_NFC_A
82                          | 0x10 #NfcAdapter.FLAG_READER_NFC_BARCODE
83                          | 0x80 #NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
84                          )
85_NFC_TECH_A_POLLING_OFF = (0x10 #NfcAdapter.FLAG_READER_NFC_BARCODE
86                           | 0x80 #NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
87                           )
88_NFC_TECH_A_LISTEN_ON = 0x1 #NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_A
89_NFC_TECH_F_LISTEN_ON = 0x4 #NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_F
90_NFC_LISTEN_OFF = 0x0 #NfcAdapter.FLAG_LISTEN_DISABLE
91_SERVICE_PACKAGE = "com.android.nfc.service"
92_ACCESS_SERVICE = _SERVICE_PACKAGE + ".AccessService"
93_OFFHOST_SERVICE = _SERVICE_PACKAGE + ".OffHostService"
94_LARGE_NUM_AIDS_SERVICE = _SERVICE_PACKAGE + ".LargeNumAidsService"
95_PAYMENT_SERVICE_1 = _SERVICE_PACKAGE + ".PaymentService1"
96_PAYMENT_SERVICE_2 = _SERVICE_PACKAGE + ".PaymentService2"
97_PAYMENT_SERVICE_DYNAMIC_AIDS = _SERVICE_PACKAGE + ".PaymentServiceDynamicAids"
98_PREFIX_ACCESS_SERVICE = _SERVICE_PACKAGE + ".PrefixAccessService"
99_PREFIX_PAYMENT_SERVICE_1 = _SERVICE_PACKAGE + ".PrefixPaymentService1"
100_PREFIX_TRANSPORT_SERVICE_2 = _SERVICE_PACKAGE + ".PrefixTransportService2"
101_SCREEN_OFF_PAYMENT_SERVICE = _SERVICE_PACKAGE + ".ScreenOffPaymentService"
102_SCREEN_ON_ONLY_OFF_HOST_SERVICE = _SERVICE_PACKAGE + ".ScreenOnOnlyOffHostService"
103_THROUGHPUT_SERVICE = _SERVICE_PACKAGE + ".ThroughputService"
104_TRANSPORT_SERVICE_1 = _SERVICE_PACKAGE + ".TransportService1"
105_TRANSPORT_SERVICE_2 = _SERVICE_PACKAGE + ".TransportService2"
106_POLLING_LOOP_SERVICE_1 = _SERVICE_PACKAGE + ".PollingLoopService"
107_POLLING_LOOP_SERVICE_2 = _SERVICE_PACKAGE + ".PollingLoopService2"
108
109_NUM_POLLING_LOOPS = 50
110_FAILED_TAG_MSG =  "Reader did not detect tag, transaction not attempted."
111_FAILED_TRANSACTION_MSG = "Transaction failed, check device logs for more information."
112
113_FRAME_EVENT_TIMEOUT_SEC = 1
114_POLLING_FRAME_TIMESTAMP_TOLERANCE_MS = 5
115_POLLING_FRAME_TIMESTAMP_EXCEED_COUNT_TOLERANCE_ = 3
116_FAILED_MISSING_POLLING_FRAMES_MSG = "Device did not receive all polling frames"
117_FAILED_TIMESTAMP_TOLERANCE_EXCEEDED_MSG = "Polling frame timestamp tolerance exceeded"
118_FAILED_VENDOR_GAIN_VALUE_DROPPED_ON_POWER_INCREASE = """
119Polling frame vendor specific gain value dropped on power increase
120"""
121_FAILED_FRAME_TYPE_INVALID = "Polling frame type is invalid"
122_FAILED_FRAME_DATA_INVALID = "Polling frame data is invalid"
123
124
125
126class CtsNfcHceMultiDeviceTestCases(base_test.BaseTestClass):
127
128    def _set_up_emulator(self, *args, start_emulator_fun=None, service_list=[],
129                 expected_service=None, is_payment=False, preferred_service=None,
130                 payment_default_service=None, should_disable_services_on_destroy=True):
131        """
132        Sets up emulator device for multidevice tests.
133        :param args: arguments for start_emulator_fun, if any
134        :param start_emulator_fun: fun
135            Custom function to start the emulator activity. If not present,
136            startSimpleEmulatorActivity will be used.
137        :param service_list: list
138            List of services to set up. Only used if a custom function is not called.
139        :param expected_service: String
140            Class name of the service expected to handle the APDUs.
141        :param is_payment: bool
142            Whether test is setting up payment services. If so, this function will register
143            this app as the default wallet.
144        :param preferred_service: String
145            Service to set as preferred service, if any.
146        :param payment_default_service: String
147            For payment tests only: the default payment service that is expected to handle APDUs.
148        :param should_disable_services_on_destroy: bool
149            Whether to disable services on destroy (set to False for reboot tests).
150
151        :return:
152        """
153        role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
154            'RoleHeld')
155        if start_emulator_fun is not None:
156            start_emulator_fun(*args)
157        else:
158            if preferred_service is None:
159                self.emulator.nfc_emulator.startSimpleEmulatorActivity(
160                        service_list, expected_service, is_payment,
161                        should_disable_services_on_destroy)
162            else:
163                self.emulator.nfc_emulator.startSimpleEmulatorActivityWithPreferredService(
164                        service_list, expected_service, preferred_service, is_payment)
165
166        if is_payment:
167            role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
168            if payment_default_service is None:
169                raise Exception("Must define payment_default_service for payment tests.")
170            self.emulator.nfc_emulator.waitForService(payment_default_service)
171
172    def _set_up_reader_and_assert_transaction(self, expected_service=None):
173        """
174        Sets up reader, and asserts successful APDU transaction
175        :param expected_service: string
176                Class name of the service expected to handle the APDUs on the emulator device.
177        :return:
178        """
179        if expected_service is None:
180            raise Exception('expected_service must be defined.')
181        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator, expected_service)
182        tag_detected, transacted = poll_and_transact(self.pn532, command_apdus, response_apdus)
183        asserts.assert_true(tag_detected, _FAILED_TAG_MSG)
184        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
185
186    def _is_cuttlefish_device(self, ad: android_device.AndroidDevice) -> bool:
187        product_name = ad.adb.getprop("ro.product.name")
188        return "cf_x86" in product_name
189
190    def _reboot(self, ad: android_device.AndroidDevice):
191        ad.reboot()
192        ad.nfc_emulator.turnScreenOn()
193        ad.nfc_emulator.pressMenu()
194
195    def _get_casimir_id_for_device(self):
196        host = "localhost"
197        conn = HTTPSConnection(host, 1443, context=ssl._create_unverified_context())
198        path = '/devices'
199        headers = {'Content-type': 'application/json'}
200        conn.request("GET", path, {}, headers)
201        response = conn.getresponse()
202        json_obj = json.loads(response.read())
203        first_device = json_obj[0]
204        return first_device["device_id"]
205
206    def setup_class(self):
207        """
208        Sets up class by registering an emulator device, enabling NFC, and loading snippets.
209
210        If a PN532 serial path is found, it uses this to configure the device. Otherwise, set up a
211        second phone as a reader device.
212        """
213        self.pn532 = None
214
215        # This tracks the error message for a setup failure.
216        # It is set to None only if the entire setup_class runs successfully.
217        self._setup_failure_reason = 'Failed to find Android device(s).'
218
219        # Indicates if the setup failure should block (FAIL) or not block (SKIP) test cases.
220        # Blocking failures indicate that something unexpectedly went wrong during test setup,
221        # and the user should have it fixed.
222        # Non-blocking failures indicate that the device(s) did not meet the test requirements,
223        # and the test does not need to be run.
224        self._setup_failure_should_block_tests = True
225
226        try:
227            devices = self.register_controller(android_device)[:1]
228            self.emulator = devices[0]
229
230            self._setup_failure_reason = (
231                'Cannot load emulator snippet. Is NfcEmulatorTestApp.apk '
232                'installed on the emulator?'
233            )
234            self.emulator.load_snippet(
235                'nfc_emulator', 'com.android.nfc.emulator'
236            )
237            self.emulator.debug_tag = 'emulator'
238            try:
239                self.emulator.adb.shell(['svc', 'nfc', 'enable'])
240            except adb.AdbError:
241                _LOG.info("Could not enable nfc through adb.")
242                self.emulator.nfc_emulator.setNfcState(True)
243            if (
244                hasattr(self.emulator, 'dimensions')
245                and 'pn532_serial_path' in self.emulator.dimensions
246            ):
247                pn532_serial_path = self.emulator.dimensions["pn532_serial_path"]
248            else:
249                pn532_serial_path = self.user_params.get("pn532_serial_path", "")
250
251            casimir_id = None
252            if self._is_cuttlefish_device(self.emulator):
253                self._setup_failure_reason = ('Failed to set up casimir connection for Cuttlefish '
254                                              'device')
255                casimir_id = self._get_casimir_id_for_device()
256
257            if casimir_id is not None and len(casimir_id) > 0:
258                self._setup_failure_reason = 'Failed to connect to casimir'
259                _LOG.info("casimir_id = " + casimir_id)
260                self.pn532 = pn532.Casimir(casimir_id)
261            else:
262                self._setup_failure_reason = 'Failed to connect to PN532 board.'
263                self.pn532 = pn532.PN532(pn532_serial_path)
264                self.pn532.mute()
265
266        except Exception as e:
267            _LOG.warning('setup_class failed with error %s', e)
268            return
269        self._setup_failure_reason = None
270
271    def setup_test(self):
272        """
273        Turns emulator screen on and unlocks between tests as some tests will turn the screen off.
274        """
275        if self._setup_failure_should_block_tests:
276            asserts.assert_true(
277                self._setup_failure_reason is None, self._setup_failure_reason
278            )
279        else:
280            asserts.skip_if(
281                self._setup_failure_reason is not None, self._setup_failure_reason
282            )
283
284        self.emulator.nfc_emulator.logInfo("*** TEST START: " + self.current_test_info.name +
285                                           " ***")
286        self.emulator.nfc_emulator.turnScreenOn()
287        self.emulator.nfc_emulator.pressMenu()
288
289    def on_fail(self, record):
290        if self.user_params.get('take_bug_report_on_fail', False):
291            test_name = record.test_name
292            if hasattr(self, 'emulator') and hasattr(self.emulator, 'nfc_emulator'):
293                self.emulator.take_bug_report(
294                    test_name=self.emulator.debug_tag + "_" + test_name,
295                    destination=self.current_test_info.output_path,
296                )
297
298    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
299    def test_single_non_payment_service(self):
300        """Tests successful APDU exchange between non-payment service and
301        reader.
302
303        Test Steps:
304        1. Start emulator activity and set up non-payment HCE Service.
305        2. Set callback handler on emulator for when a TestPass event is
306        received.
307        3. Set up PN532, which should trigger APDU exchange.
308
309        Verifies:
310        1. Verifies a successful APDU exchange.
311        """
312        self._set_up_emulator(
313            service_list=[_TRANSPORT_SERVICE_1],
314            expected_service=_TRANSPORT_SERVICE_1
315        )
316
317        self._set_up_reader_and_assert_transaction(expected_service=_TRANSPORT_SERVICE_1)
318
319    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"])
320    def test_single_payment_service(self):
321        """Tests successful APDU exchange between payment service and
322        reader.
323
324        Test Steps:
325        1. Set callback handler on emulator for when the instrumentation app is
326        set to default wallet app.
327        2. Start emulator activity and wait for the app to hold the wallet role.
328        3. Start PN532 reader, which should trigger APDU exchange between
329        reader and emulator.
330
331        Verifies:
332        1. Verifies emulator device sets the instrumentation emulator app to the
333        default wallet app.
334        2. Verifies a successful APDU exchange.
335        """
336        self._set_up_emulator(
337            service_list=[_PAYMENT_SERVICE_1],
338            expected_service=_PAYMENT_SERVICE_1,
339            is_payment=True,
340            payment_default_service=_PAYMENT_SERVICE_1
341        )
342
343        self._set_up_reader_and_assert_transaction(expected_service=_PAYMENT_SERVICE_1)
344
345    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"])
346    def test_single_payment_service_after_reboot(self):
347        """Tests successful APDU exchange between payment service and
348        reader after a reboot.
349
350        Test Steps:
351        1. Set callback handler on emulator for when the instrumentation app is
352        set to default wallet app.
353        2. Reboot the emulator device.
354        3. Start emulator activity and wait for the app to hold the wallet role.
355        4. Start PN532 reader, which should trigger APDU exchange between
356        reader and emulator.
357
358        Verifies:
359        1. Verifies emulator device sets the instrumentation emulator app to the
360        default wallet app.
361        2. Verifies a successful APDU exchange after reboot.
362        """
363        # Set the role before rebooting and ensure it remains enabled after
364        # reboot to ensure that the NFC stack binds to it at bootup.
365        self._set_up_emulator(
366            service_list=[_PAYMENT_SERVICE_1],
367            expected_service=_PAYMENT_SERVICE_1,
368            is_payment=True, # Set the role holder before reboot.
369            payment_default_service=_PAYMENT_SERVICE_1,
370            should_disable_services_on_destroy=False # Don't disable services on shutdown.
371        )
372        self._reboot(self.emulator)
373        # Setup the payment service activity to handle the transaction after
374        # reboot.
375        self._set_up_emulator(
376            service_list=[_PAYMENT_SERVICE_1],
377            expected_service=_PAYMENT_SERVICE_1,
378            is_payment=False, # Don't set the role to ensure that state is persisted across reboot.
379            payment_default_service=_PAYMENT_SERVICE_1
380        )
381        self._set_up_reader_and_assert_transaction(expected_service=_PAYMENT_SERVICE_1)
382
383
384    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"])
385    def test_single_payment_service_with_background_app(self):
386        """Tests successful APDU exchange between payment service and
387        reader.
388
389        Test Steps:
390        1. Start emulator activity and wait for the app to hold the wallet role.
391        2. Set callback handler on emulator for when a TestPass event is
392        received.
393        3. Move emulator activity to the background by sending a Home key event.
394        4. Start PN532, which should trigger APDU exchange with the emulator.
395
396        Verifies:
397        1. Verifies emulator device sets the instrumentation emulator app to the
398        default wallet app.
399        2. Verifies a successful APDU exchange.
400        """
401        self._set_up_emulator(
402            service_list=[_PAYMENT_SERVICE_1],
403            expected_service=_PAYMENT_SERVICE_1,
404            is_payment=True,
405            payment_default_service=_PAYMENT_SERVICE_1
406        )
407
408        self.emulator.nfc_emulator.pressHome()
409
410        self._set_up_reader_and_assert_transaction(expected_service=_PAYMENT_SERVICE_1)
411
412    def test_single_payment_service_crashes(self):
413        """Tests successful APDU exchange between payment service and
414        reader.
415
416        Test Steps:
417        1. Set callback handler on emulator for when the instrumentation app is
418        set to default wallet app.
419        2. Start emulator activity and wait for the role to be set.
420        2. Set callback handler on emulator for when a TestPass event is
421        received.
422        3. Start PN532, which should trigger APDU exchange with the emulator.
423
424        Verifies:
425        1. Verifies emulator device sets the instrumentation emulator app to the
426        default wallet app.
427        2. Verifies a successful APDU exchange.
428        """
429        self._set_up_emulator(
430            service_list=[_PAYMENT_SERVICE_1],
431            expected_service=_PAYMENT_SERVICE_1,
432            is_payment=True,
433            payment_default_service=_PAYMENT_SERVICE_1
434        )
435
436        ps = (self.emulator.adb.shell(["ps", "|", "grep", "com.android.nfc.emulator.payment"])
437              .decode("utf-8"))
438        pid = ps.split()[1]
439        try:
440            self.emulator.adb.shell(["kill", "-9", pid])
441        except adb.AdbError:
442            _LOG.info(f"Could not kill pid {pid} through adb.")
443            self.emulator.nfc_emulator.killProcess(pid)
444
445        self._set_up_reader_and_assert_transaction(expected_service=_PAYMENT_SERVICE_1)
446
447    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"])
448    def test_dual_payment_service(self):
449        """Tests successful APDU exchange between a payment service and
450        reader when two payment services are set up on the emulator.
451
452        Test Steps:
453        1. Start emulator activity and wait for the role to be set.
454        2. Set callback handler on emulator for when a TestPass event is
455        received.
456        3. Start reader activity, which should trigger APDU exchange between
457        reader and emulator.
458
459        Verifies:
460        1. Verifies a successful APDU exchange.
461        """
462        self._set_up_emulator(
463            service_list=[_PAYMENT_SERVICE_1,_PAYMENT_SERVICE_2],
464            expected_service=_PAYMENT_SERVICE_1,
465            is_payment=True,
466            payment_default_service=_PAYMENT_SERVICE_1
467        )
468
469        self._set_up_reader_and_assert_transaction(expected_service=_PAYMENT_SERVICE_1)
470
471    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"])
472    def test_foreground_payment_emulator(self):
473        """Tests successful APDU exchange between non-default payment service and
474        reader when the foreground app sets a preference for the non-default
475        service.
476
477        Test Steps:
478        1. Start emulator activity and wait for the role to be set.
479        2. Set callback handler on emulator for when a TestPass event is
480        received.
481        3. Start reader activity, which should trigger APDU exchange between
482        reader and emulator.
483
484        Verifies:
485        1. Verifies a successful APDU exchange.
486        """
487        self._set_up_emulator(
488            service_list=[_PAYMENT_SERVICE_1, _PAYMENT_SERVICE_2],
489            preferred_service=_PAYMENT_SERVICE_2,
490            expected_service=_PAYMENT_SERVICE_2,
491            is_payment=True,
492            payment_default_service=_PAYMENT_SERVICE_2
493        )
494
495        self._set_up_reader_and_assert_transaction(expected_service=_PAYMENT_SERVICE_2)
496
497    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
498    def test_dynamic_aid_emulator(self):
499        """Tests successful APDU exchange between payment service and reader
500        when the payment service has registered dynamic AIDs.
501
502        Test Steps:
503        1. Start emulator activity and wait for the role to be set.
504        2. Set callback handler on emulator for when a TestPass event is
505        received.
506        3. Start reader activity, which should trigger APDU exchange between
507        reader and emulator.
508
509        Verifies:
510        1. Verifies a successful APDU exchange.
511        """
512        self._set_up_emulator(
513            start_emulator_fun=self.emulator.nfc_emulator.startDynamicAidEmulatorActivity,
514            payment_default_service=_PAYMENT_SERVICE_DYNAMIC_AIDS
515        )
516
517        self._set_up_reader_and_assert_transaction(expected_service=_PAYMENT_SERVICE_DYNAMIC_AIDS)
518
519    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"])
520    def test_payment_prefix_emulator(self):
521        """Tests successful APDU exchange between payment service and reader
522        when the payment service has statically registered prefix AIDs.
523
524        Test Steps:
525        1. Start emulator activity and wait for the role to be set.
526        2. Set callback handler on emulator for when a TestPass event is
527        received.
528        3. Start reader activity, which should trigger APDU exchange between
529        reader and emulator.
530
531        Verifies:
532        1. Verifies a successful APDU exchange.
533        """
534        asserts.skip_if(not self.emulator.nfc_emulator.isAidPrefixRegistrationSupported(),
535            "Prefix registration is not supported on device")
536        self._set_up_emulator(
537            start_emulator_fun=self.emulator.nfc_emulator.startPrefixPaymentEmulatorActivity,
538            payment_default_service=_PREFIX_PAYMENT_SERVICE_1,
539            is_payment=True
540        )
541
542        self._set_up_reader_and_assert_transaction(expected_service=_PREFIX_PAYMENT_SERVICE_1)
543
544    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"])
545    def test_prefix_payment_emulator_2(self):
546        """Tests successful APDU exchange between payment service and reader
547        when the payment service has statically registered prefix AIDs.
548        Identical to the test above, except PrefixPaymentService2 is set up
549        first in the emulator activity.
550
551        Test Steps:
552        1. Start emulator activity and wait for the role to be set.
553        2. Set callback handler on emulator for when a TestPass event is
554        received.
555        3. Start reader activity, which should trigger APDU exchange between
556        reader and emulator.
557
558        Verifies:
559        1. Verifies a successful APDU exchange.
560        """
561        asserts.skip_if(not self.emulator.nfc_emulator.isAidPrefixRegistrationSupported(),
562            "Prefix registration is not supported on device")
563        self._set_up_emulator(
564            start_emulator_fun=self.emulator.nfc_emulator.startPrefixPaymentEmulator2Activity,
565            payment_default_service=_PREFIX_PAYMENT_SERVICE_1,
566            is_payment=True
567        )
568
569        self._set_up_reader_and_assert_transaction(expected_service=_PREFIX_PAYMENT_SERVICE_1)
570
571    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
572    def test_other_prefix(self):
573        """Tests successful APDU exchange when the emulator dynamically
574        registers prefix AIDs for a non-payment service.
575
576        Test steps:
577        1. Start emulator activity.
578        2. Set callback handler on emulator for when ApduSuccess event is
579        received.
580        3. Start reader activity, which should trigger APDU exchange between
581        reader and emulator.
582
583        Verifies:
584        1. Verifies successful APDU sequence exchange.
585
586        """
587        asserts.skip_if(not self.emulator.nfc_emulator.isAidPrefixRegistrationSupported(),
588            "Prefix registration is not supported on device")
589        self._set_up_emulator(
590            start_emulator_fun=self.emulator.nfc_emulator.startDualNonPaymentPrefixEmulatorActivity)
591
592        self._set_up_reader_and_assert_transaction(expected_service=_PREFIX_ACCESS_SERVICE)
593
594    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
595    def test_offhost_service(self):
596        """Tests successful APDU exchange between offhost service and reader.
597
598        Test Steps:
599        1. Start emulator activity.
600        2. Set callback handler for when reader TestPass event is received.
601        3. Start reader activity, which should trigger APDU exchange between
602        reader and emulator.
603
604        Verifies:
605        1. Verifies a successful APDU exchange.
606        """
607        self._set_up_emulator(
608            False, start_emulator_fun=self.emulator.nfc_emulator.startOffHostEmulatorActivity)
609
610        self._set_up_reader_and_assert_transaction(expected_service=_OFFHOST_SERVICE)
611
612    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
613    def test_on_and_offhost_service(self):
614        """Tests successful APDU exchange between when reader selects both an on-host and off-host
615        service.
616
617        Test Steps:
618        1. Start emulator activity.
619        2. Set callback handler for when reader TestPass event is received.
620        3. Start reader activity, which should trigger APDU exchange between
621        reader and emulator.
622
623        Verifies:
624        1. Verifies a successful APDU exchange.
625        """
626        self._set_up_emulator(
627            start_emulator_fun=self.emulator.nfc_emulator.startOnAndOffHostEmulatorActivity)
628
629        self._set_up_reader_and_assert_transaction(expected_service=_TRANSPORT_SERVICE_1)
630
631    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
632    def test_dual_non_payment(self):
633        """Tests successful APDU exchange between transport service and reader
634        when two non-payment services are enabled.
635
636        Test Steps:
637        1. Start emulator activity which sets up TransportService2 and
638        AccessService.
639        2. Start PN532, which should trigger APDU exchange between
640        reader and emulator.
641
642        Verifies:
643        1. Verifies a successful APDU exchange.
644        """
645        self._set_up_emulator(
646            service_list=[_TRANSPORT_SERVICE_2, _ACCESS_SERVICE],
647            expected_service=_TRANSPORT_SERVICE_2,
648            is_payment=False
649        )
650
651        self._set_up_reader_and_assert_transaction(expected_service = _TRANSPORT_SERVICE_2)
652
653    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
654    def test_foreground_non_payment(self):
655        """Tests successful APDU exchange between non-payment service and
656          reader when the foreground app sets a preference for the
657          non-default service.
658
659          Test Steps:
660          1. Start emulator activity which sets up TransportService1 and
661          TransportService2
662          2. Start PN532, which should trigger APDU exchange between
663          reader and non-default service.
664
665          Verifies:
666          1. Verifies a successful APDU exchange.
667          """
668        self._set_up_emulator(
669            service_list=[_TRANSPORT_SERVICE_1, _TRANSPORT_SERVICE_2],
670            preferred_service=_TRANSPORT_SERVICE_2,
671            expected_service=_TRANSPORT_SERVICE_2,
672            is_payment=False
673        )
674
675        self._set_up_reader_and_assert_transaction(
676            expected_service=_TRANSPORT_SERVICE_2)
677
678    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
679    def test_throughput(self):
680        """Tests that APDU sequence exchange occurs with under 60ms per APDU.
681
682         Test Steps:
683         1. Start emulator activity.
684         2. Set callback handler on emulator for when a TestPass event is
685         received.
686         3. Start reader activity, which should trigger APDU exchange between
687         reader and non-default service.
688
689         Verifies:
690         1. Verifies a successful APDU exchange between the emulator and the
691         transport service with the duration averaging under 60 ms per single
692         exchange.
693         """
694        asserts.skip_if("_cf_x86_" in self.emulator.adb.getprop("ro.product.name"),
695                        "Skipping throughput test on Cuttlefish")
696        self.emulator.nfc_emulator.startThroughputEmulatorActivity()
697        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
698            'ApduUnderThreshold')
699        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
700                                                  _THROUGHPUT_SERVICE)
701        poll_and_transact(self.pn532, command_apdus, response_apdus)
702
703        test_pass_handler.waitAndGet('ApduUnderThreshold', _NFC_TIMEOUT_SEC)
704
705    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
706    def test_tap_50_times(self):
707        """Tests that 50 consecutive APDU exchanges are successful.
708
709        Test Steps:
710         1. Start emulator activity.
711         2. Perform the following sequence 50 times:
712            a. Set callback handler on emulator for when a TestPass event is
713            received.
714            b. Start PN532.
715            c. Wait for successful APDU exchange.
716            d. Close reader activity.
717
718         Verifies:
719         1. Verifies 50 successful APDU exchanges.
720         """
721        self._set_up_emulator(
722            service_list=[_TRANSPORT_SERVICE_1],
723            expected_service=_TRANSPORT_SERVICE_1
724        )
725
726        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
727                                                  _TRANSPORT_SERVICE_1)
728        for i in range(50):
729            tag_detected, transacted = poll_and_transact(self.pn532, command_apdus,
730                                                         response_apdus)
731            asserts.assert_true(
732                tag_detected, _FAILED_TAG_MSG
733            )
734            asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
735
736    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
737    def test_large_num_aids(self):
738        """Tests that a long APDU sequence (256 commands/responses) is
739        successfully exchanged.
740
741        Test Steps:
742         1. Start emulator activity.
743         2. Set callback handler on emulator for when a TestPass event is
744         received.
745         3. Start reader activity.
746         4. Wait for successful APDU exchange.
747
748         Verifies:
749         1. Verifies successful APDU exchange.
750         """
751        self._set_up_emulator(
752            start_emulator_fun=self.emulator.nfc_emulator.startLargeNumAidsEmulatorActivity
753        )
754
755        self._set_up_reader_and_assert_transaction(expected_service=_LARGE_NUM_AIDS_SERVICE)
756
757    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
758    def test_screen_off_payment(self):
759        """Tests that APDU exchange occurs when device screen is off.
760
761        Test Steps:
762        1. Start emulator activity and wait for the role to be set.
763        2. Turn emulator screen off.
764        3. Start PN532, which should trigger successful APDU exchange.
765
766        Verifies:
767        1. Verifies default wallet app is set.
768        2. Verifies screen is turned off on the emulator.
769        3. Verifies successful APDU exchange with emulator screen off.
770        """
771        self._set_up_emulator(
772            start_emulator_fun=self.emulator.nfc_emulator.startScreenOffPaymentEmulatorActivity,
773            payment_default_service=_SCREEN_OFF_PAYMENT_SERVICE,
774            is_payment=True
775        )
776
777        screen_off_handler = self.emulator.nfc_emulator.asyncWaitForScreenOff(
778            'ScreenOff')
779        self.emulator.nfc_emulator.turnScreenOff()
780        screen_off_handler.waitAndGet('ScreenOff', _NFC_TIMEOUT_SEC)
781
782        self._set_up_reader_and_assert_transaction(expected_service=_SCREEN_OFF_PAYMENT_SERVICE)
783
784    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
785    def test_conflicting_non_payment(self):
786        """ This test registers two non-payment services with conflicting AIDs,
787        selects a service to use, and ensures the selected service exchanges
788        an APDU sequence with the reader.
789
790        Test Steps:
791        1. Start emulator.
792        2. Start PN532 reader, which should trigger a popup screen of NFC services to select from.
793        3. Select a service on the emulator device from the list of services.
794        4. Set a callback handler on the emulator for a successful APDU
795        exchange.
796        6. Set up reader for transaction, which should trigger the APDU
797        exchange with the selected service.
798
799        Verifies:
800        1. Verifies APDU exchange is successful between the reader and the
801        selected service.
802        """
803        self._set_up_emulator(service_list=[_TRANSPORT_SERVICE_1,_TRANSPORT_SERVICE_2],
804                              expected_service=_TRANSPORT_SERVICE_2, is_payment=False)
805        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator, _TRANSPORT_SERVICE_2)
806        poll_and_transact(self.pn532, command_apdus[:1], response_apdus[:1])
807
808        self.emulator.nfc_emulator.selectItem()
809
810        tag_detected, transacted = poll_and_transact(self.pn532, command_apdus, response_apdus)
811        asserts.assert_true(
812            tag_detected, _FAILED_TAG_MSG
813        )
814        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
815
816
817    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
818
819    @ApiTest(
820            apis = {
821                "android.nfc.cardemulation.CardEmulation.NfcEventCallback#onAidConflictOccurred"
822            })
823    def test_conflicting_non_payment_prefix(self):
824        """ This test registers two non-payment services with conflicting
825        prefix AIDs, selects a service to use, and ensures the selected
826        service exchanges an APDU sequence with the reader.
827
828        Test Steps:
829        1. Start emulator.
830        2. Start PN532.
831        3. Select a service on the emulator device from the list of services.
832        4. Disable polling on the reader.
833        5. Re-enable polling on the reader, which should trigger the APDU
834        exchange with the selected service.
835
836        Verifies:
837        1. Verifies APDU exchange is successful between the reader and the
838        selected service.
839        """
840        asserts.skip_if(not self.emulator.nfc_emulator.isAidPrefixRegistrationSupported(),
841            "Prefix registration is not supported on device")
842        self._set_up_emulator(
843            start_emulator_fun=
844                self.emulator.nfc_emulator.startConflictingNonPaymentPrefixEmulatorActivity,
845            is_payment=False
846        )
847        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
848                                                  _PREFIX_TRANSPORT_SERVICE_2)
849        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
850            'ApduSuccess'
851        )
852        poll_and_transact(self.pn532, command_apdus[:1], response_apdus[:1])
853
854        self.emulator.nfc_emulator.selectItem()
855        tag_detected, transacted = poll_and_transact(self.pn532, command_apdus, response_apdus)
856        asserts.assert_true(tag_detected, _FAILED_TAG_MSG)
857        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
858
859        test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
860
861    #@CddTest(requirements = {"TODO"})
862    @ApiTest(
863            apis = {
864                "android.nfc.cardemulation.CardEmulation.NfcEventCallback#onPreferredServiceChanged",
865                "android.nfc.cardemulation.CardEmulation.NfcEventCallback#onRemoteFieldChanged"
866            })
867    def test_event_listener(self):
868        """ This test registers an event listener with the emulator and ensures
869        that the event listener receives callbacks when the field status changes and
870        when the preferred service changes.
871
872        Test Steps:
873        1. Start the emulator.
874        2. Start PN532.
875        3. Select a routed AID on the emulator.
876
877        Verifies:
878        1. Verifies that the event listener receives callbacks when the field
879        status changes and when the preferred service changes.
880        """
881        self._set_up_emulator(
882            start_emulator_fun=self.emulator.nfc_emulator.startEventListenerActivity,
883            is_payment=False
884        )
885        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
886            "EventListenerSuccess"
887        )
888
889        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator,
890                                                    _TRANSPORT_SERVICE_1)
891        tag_detected, transacted = poll_and_transact(self.pn532, command_apdus, response_apdus)
892        asserts.assert_true(tag_detected, _FAILED_TAG_MSG)
893        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
894        test_pass_handler.waitAndGet("EventListenerSuccess", _NFC_TIMEOUT_SEC)
895
896
897    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
898    def test_protocol_params(self):
899        """ Tests that the Nfc-A and ISO-DEP protocol parameters are being
900        set correctly.
901
902        Test Steps:
903        1. Start emulator.
904        2. Start PN532.
905        4. Wait for success event to be sent.
906
907        Verifies:
908        1. Verifies Nfc-A and ISO-DEP protocol parameters are set correctly.
909        """
910        success = False
911        self._set_up_emulator(
912            service_list=[],
913            expected_service=""
914        )
915
916        for i in range(_NUM_POLLING_LOOPS):
917            tag = self.pn532.poll_a()
918            msg = None
919            if tag is not None:
920                success, msg = parse_protocol_params(tag.sel_res, tag.ats)
921                self.pn532.mute()
922                break
923            self.pn532.mute()
924        asserts.assert_true(success, msg if msg is not None else _FAILED_TAG_MSG)
925
926
927    @CddTest(requirements = ["7.4.4/C-2-2", "7.4.4/C-1-2"])
928    def test_screen_on_only_off_host_service(self):
929        """
930        Test Steps:
931        1. Start emulator.
932        2. Turn screen off.
933        3. Start PN532, and ensure expected APDU exchange occurs.
934        4. Turn screen off.
935        5. Start PN532, and ensure expected APDU exchange occurs.
936
937
938        Verifies:
939        1. Verifies correct APDU response when screen is off.
940        2. Verifies correct APDU response between reader and off-host service
941        when screen is on.
942        """
943        #Tests APDU exchange with screen off.
944        self._set_up_emulator(
945            start_emulator_fun=self.emulator.nfc_emulator.startScreenOnOnlyOffHostEmulatorActivity
946        )
947        self.emulator.nfc_emulator.turnScreenOff()
948        screen_off_handler = self.emulator.nfc_emulator.asyncWaitForScreenOff(
949            'ScreenOff')
950        screen_off_handler.waitAndGet('ScreenOff', _NFC_TIMEOUT_SEC)
951
952        self._set_up_reader_and_assert_transaction(
953            expected_service=_SCREEN_ON_ONLY_OFF_HOST_SERVICE)
954
955        self.pn532.mute()
956
957        #Tests APDU exchange with screen on.
958        screen_on_handler = self.emulator.nfc_emulator.asyncWaitForScreenOn(
959            'ScreenOn')
960        self.emulator.nfc_emulator.pressMenu()
961        screen_on_handler.waitAndGet('ScreenOn', _NFC_TIMEOUT_SEC)
962
963        self._set_up_reader_and_assert_transaction(expected_service=
964                                                   _SCREEN_ON_ONLY_OFF_HOST_SERVICE)
965
966    def test_single_payment_service_toggle_nfc_off_on(self):
967        """Tests successful APDU exchange between payment service and
968        reader.
969
970        Test Steps:
971        1. Start emulator activity and wait for the role to be set.
972        2. Toggle NFC off and back on the emulator.
973        3. Set callback handler on emulator for when a TestPass event is
974        received.
975        4. Start reader activity, which should trigger APDU exchange between
976        reader and emulator.
977
978        Verifies:
979        1. Verifies emulator device sets the instrumentation emulator app to the
980        default wallet app.
981        2. Verifies a successful APDU exchange after toggling NFC off and on.
982        """
983        # Wait for instrumentation app to hold onto wallet role before starting
984        # reader
985        self._set_up_emulator(
986            service_list=[_PAYMENT_SERVICE_1],
987            expected_service=_PAYMENT_SERVICE_1,
988            is_payment=True,
989            payment_default_service=_PAYMENT_SERVICE_1
990        )
991
992        self.emulator.nfc_emulator.setNfcState(False)
993        self.emulator.nfc_emulator.setNfcState(True)
994
995        self._set_up_reader_and_assert_transaction(expected_service=_PAYMENT_SERVICE_1)
996
997    @CddTest(requirements = ["7.4.4/C-1-13"])
998    def test_polling_frame_timestamp(self):
999        """Tests that PollingFrame object timestamp values are reported correctly
1000        and do not deviate from host measurements
1001
1002        Test Steps:
1003        1. Toggle NFC reader field OFF
1004        2. Start emulator activity
1005        3. Perform a polling loop, wait for field OFF event.
1006        4. Collect polling frames. Iterate over matching polling loop frame
1007        and device time measurements. Calculate elapsed time for each and verify
1008        that the host-device difference does not exceed the delay threshold.
1009
1010        Verifies:
1011        1. Verifies that timestamp values are reported properly
1012        for each tested frame type.
1013        2. Verifies that the difference between matching host and device
1014        timestamps does not exceed _POLLING_FRAME_TIMESTAMP_TOLERANCE_MS.
1015        """
1016        asserts.skip_if(not self.emulator.nfc_emulator.isObserveModeSupported(),
1017            "Skipping polling frame timestamp test, observe mode not supported")
1018
1019        # 1. Mute the field before starting the emulator
1020        # in order to be able to trigger ON event when the test starts
1021        self.pn532.mute()
1022
1023        # 2. Start emulator activity
1024        self._set_up_emulator(
1025            start_emulator_fun=self.emulator.nfc_emulator.startPollingFrameEmulatorActivity
1026        )
1027
1028        timed_pn532 = TimedWrapper(self.pn532)
1029        testcases = [
1030            POLLING_FRAME_ON,
1031            *POLLING_FRAMES_TYPE_A_SPECIAL,
1032            *POLLING_FRAMES_TYPE_A_SPECIAL,
1033            *POLLING_FRAMES_TYPE_A_LONG,
1034            *POLLING_FRAMES_TYPE_A_LONG,
1035            *POLLING_FRAMES_TYPE_B_SPECIAL,
1036            *POLLING_FRAMES_TYPE_B_SPECIAL,
1037            *POLLING_FRAMES_TYPE_B_LONG,
1038            *POLLING_FRAMES_TYPE_B_LONG,
1039            *POLLING_FRAMES_TYPE_F_SPECIAL,
1040            *POLLING_FRAMES_TYPE_F_SPECIAL,
1041            POLLING_FRAME_OFF,
1042        ]
1043        # 3. Transmit polling frames
1044        frames = poll_and_observe_frames(
1045            pn532=timed_pn532,
1046            emulator=self.emulator.nfc_emulator,
1047            testcases=testcases,
1048            restore_original_frame_ordering=True
1049        )
1050        timings = timed_pn532.timings
1051
1052        # Pre-format data for error if one happens
1053        frame_stats = get_frame_test_stats(
1054            frames=frames,
1055            testcases=testcases,
1056            timestamps=[ns_to_us(timestamp) for (_, timestamp) in timings]
1057        )
1058
1059        # Check that there are as many polling loop events as frames sent
1060        asserts.assert_equal(
1061            len(testcases), len(frames),
1062            _FAILED_MISSING_POLLING_FRAMES_MSG,
1063            frame_stats
1064        )
1065
1066        # For each event, calculate the amount of time elapsed since the previous one
1067        # Subtract the resulting host/device time delta values
1068        # Verify that the difference does not exceed the threshold
1069        previous_timestamp_device = None
1070        first_timestamp_start, first_timestamp_end = timings[0]
1071        first_timestamp = (first_timestamp_start + first_timestamp_end) / 2
1072        first_timestamp_error = (first_timestamp_end - first_timestamp_start)/ 2
1073        first_timestamp_device = frames[0].timestamp
1074
1075        num_exceeding_threshold = 0
1076        for idx, (frame, timing, testcase) in enumerate(zip(frames, timings, testcases)):
1077            timestamp_host_start, timestamp_host_end = timing
1078            timestamp_host = (timestamp_host_start + timestamp_host_end) / 2
1079            timestamp_error = (timestamp_host_end - timestamp_host_start) / 2
1080            timestamp_device = frame.timestamp
1081
1082            _LOG.debug(
1083               f"{testcase.data.upper():32}" + \
1084               f" ({testcase.configuration.type}" + \
1085               f", {'+' if testcase.configuration.crc else '-'}" + \
1086               f", {testcase.configuration.bits})" + \
1087               f" -> {frame.data.hex().upper():32} ({frame.type})"
1088            )
1089
1090            pre_previous_timestamp_device = previous_timestamp_device
1091            previous_timestamp_device = timestamp_device
1092
1093            # Skip cases when timestamp value wraps
1094            # as there's no way to establish the delta
1095            # and re-establish the baselines
1096            if (timestamp_device - first_timestamp_device) < 0:
1097                _LOG.warning(
1098                    "Timestamp value wrapped" + \
1099                    f" from {pre_previous_timestamp_device}" + \
1100                    f" to {previous_timestamp_device} for frame {frame};" + \
1101                    " Skipping comparison..."
1102                )
1103                first_timestamp_device = timestamp_device
1104                first_timestamp = timestamp_host
1105                first_timestamp_error = timestamp_error
1106                continue
1107
1108            device_host_difference = us_to_ms(timestamp_device - first_timestamp_device) - \
1109                ns_to_ms(timestamp_host - first_timestamp)
1110            total_error = ns_to_ms(timestamp_error + first_timestamp_error)
1111            if abs(device_host_difference) > _POLLING_FRAME_TIMESTAMP_TOLERANCE_MS + total_error:
1112                debug_info = {
1113                    "index": idx,
1114                    "frame_sent": testcase.format_for_error(timestamp=ns_to_us(timestamp_host)),
1115                    "frame_received": frame.to_dict(),
1116                    "difference": device_host_difference,
1117                    "time_device": us_to_ms(timestamp_device - first_timestamp_device),
1118                    "time_host": ns_to_ms(timestamp_host - first_timestamp),
1119                    "total_error": total_error,
1120                }
1121                num_exceeding_threshold = num_exceeding_threshold + 1
1122                _LOG.warning(f"Polling frame timestamp tolerance exceeded: {debug_info}")
1123        asserts.assert_less(num_exceeding_threshold,
1124                                  _POLLING_FRAME_TIMESTAMP_EXCEED_COUNT_TOLERANCE_)
1125
1126    @CddTest(requirements = ["7.4.4/C-1-13"])
1127    def test_polling_frame_vendor_specific_gain(self):
1128        """Tests that PollingFrame object vendorSpecificGain value
1129        changes when overall NFC reader output power changes
1130
1131        Test Steps:
1132        1. Toggle NFC reader field OFF
1133        2. Start emulator activity
1134        3. For each power level (0-100% with 20% step), send polling loop
1135        consisting of normally encountered polling frames
1136        4. For each result, calculate average vendorSpecificGain per NFC mode
1137        compare those values against the previous power step, and assert that
1138        they are equal or bigger than the previous one
1139
1140        Verifies:
1141        1. Verifies that vendorSpecificGain value increases or stays the same
1142        when PN532 output power increases.
1143        """
1144        asserts.skip_if(not self.emulator.nfc_emulator.isObserveModeSupported(),
1145                    "Skipping polling frame gain test, observe mode not supported")
1146
1147        self.pn532.mute()
1148        emulator = self.emulator.nfc_emulator
1149
1150        self._set_up_emulator(
1151            start_emulator_fun=emulator.startPollingFrameEmulatorActivity
1152        )
1153
1154        # Loop two times so that HostEmulationManager releases all frames
1155        testcases = [
1156            POLLING_FRAME_ON,
1157            *POLLING_FRAMES_TYPE_A_SPECIAL,
1158            *POLLING_FRAMES_TYPE_B_SPECIAL,
1159            *POLLING_FRAMES_TYPE_F_SPECIAL,
1160            POLLING_FRAME_OFF
1161        ] * 2
1162
1163        # 6 steps; 0%, 20%, 40%, 60%, 80%, 100%
1164        power_levels = [0, 1, 2, 3, 4, 5]
1165        polling_frame_types = ("A", "B", "F")
1166
1167        results_for_power_level = {}
1168
1169        for power_level in power_levels:
1170            # Warning for 0% might appear,
1171            # as it's possible for no events to be produced
1172            frames = poll_and_observe_frames(
1173                testcases=testcases,
1174                pn532=self.pn532,
1175                emulator=emulator,
1176                # Scale from 0 to 100%
1177                power_level = power_level * 20,
1178                ignore_field_off_event_timeout=power_level == 0
1179            )
1180
1181            frames_for_type = {
1182                type_: [
1183                    f.vendor_specific_gain for f in frames if f.type == type_
1184                ] for type_ in polling_frame_types
1185            }
1186            results_for_power_level[power_level] = {
1187                # If no frames for type, assume gain is negative -1
1188                type_: (sum(frames) / len(frames)) if len(frames) else -1
1189                for type_, frames in frames_for_type.items()
1190            }
1191
1192        _LOG.debug(f"Polling frame gain results {results_for_power_level}")
1193
1194        issues = []
1195        for power_level in power_levels:
1196            # No value to compare to
1197            if power_level == 0:
1198                continue
1199
1200            for type_ in polling_frame_types:
1201                previous_gain = results_for_power_level[power_level - 1][type_]
1202                current_gain = results_for_power_level[power_level][type_]
1203                if current_gain >= previous_gain:
1204                    continue
1205                sample = {
1206                    "type": type_,
1207                    "power_level": power_level * 20,
1208                    "previous_gain": previous_gain,
1209                    "current_gain": current_gain,
1210                }
1211                _LOG.warning(
1212                    f"Reported gain level dropped" + \
1213                    f" between power steps {sample}"
1214                )
1215                issues.append(sample)
1216
1217        # Allow up to 2 reported gain decreases out of (5 * 3) = 15 test samples
1218        # Theoretically, this could happen
1219        # due to automatic power/gain/load management feature of chipsets
1220        asserts.assert_true(
1221            len(issues) <= 2,
1222            _FAILED_VENDOR_GAIN_VALUE_DROPPED_ON_POWER_INCREASE,
1223        )
1224
1225    @CddTest(requirements = ["7.4.4/C-1-13"])
1226    def test_polling_frame_type(self):
1227        """Tests that PollingFrame object 'type' value is set correctly
1228
1229        Test Steps:
1230        1. Toggle NFC reader field OFF
1231        2. Start emulator activity
1232        3. Perform a polling loop, wait for field OFF event.
1233        4. Collect polling frames. Iterate over sent and received frame pairs,
1234        verify that polling frame type matches.
1235
1236        Verifies:
1237        1. Verifies that PollingFrame.type value is set correctly
1238        """
1239        asserts.skip_if(not self.emulator.nfc_emulator.isObserveModeSupported(),
1240                    "Skipping polling frame type test, observe mode not supported")
1241        self.pn532.mute()
1242        emulator = self.emulator.nfc_emulator
1243
1244        self._set_up_emulator(
1245            start_emulator_fun=emulator.startPollingFrameEmulatorActivity
1246        )
1247
1248        testcases = POLLING_FRAME_ALL_TEST_CASES
1249
1250        # 3. Transmit polling frames
1251        frames = poll_and_observe_frames(
1252            pn532=self.pn532,
1253            emulator=self.emulator.nfc_emulator,
1254            testcases=testcases,
1255            restore_original_frame_ordering=True,
1256        )
1257
1258        # Check that there are as many polling loop events as frames sent
1259        asserts.assert_equal(
1260            len(testcases), len(frames),
1261            _FAILED_MISSING_POLLING_FRAMES_MSG,
1262            get_frame_test_stats(frames=frames, testcases=testcases)
1263        )
1264
1265        issues = [
1266            {
1267                "index": idx,
1268                "expected": testcase.success_types,
1269                "received": frame.type,
1270                "data": frame.data.hex(),
1271            } for idx, (testcase, frame) in enumerate(zip(testcases, frames))
1272            if frame.type not in testcase.success_types
1273        ]
1274
1275        asserts.assert_equal(len(issues), 0, _FAILED_FRAME_TYPE_INVALID, issues)
1276
1277    @CddTest(requirements = ["7.4.4/C-1-13"])
1278    def test_polling_frame_data(self):
1279        """Tests that PollingFrame object 'data' value is set correctly
1280
1281        Test Steps:
1282        1. Toggle NFC reader field OFF
1283        2. Start emulator activity
1284        3. Perform a polling loop, wait for field OFF event.
1285        4. Collect polling frames. Iterate over sent and received frame pairs,
1286        verify that polling frame type matches.
1287
1288        Verifies:
1289        1. Verifies that PollingFrame.data value is set correctly
1290        """
1291        asserts.skip_if(not self.emulator.nfc_emulator.isObserveModeSupported(),
1292                    "Skipping polling frame data test, observe mode not supported")
1293        self.pn532.mute()
1294        emulator = self.emulator.nfc_emulator
1295
1296        self._set_up_emulator(
1297            start_emulator_fun=emulator.startPollingFrameEmulatorActivity
1298        )
1299
1300        testcases = POLLING_FRAME_ALL_TEST_CASES
1301
1302        # 3. Transmit polling frames
1303        frames = poll_and_observe_frames(
1304            pn532=self.pn532,
1305            emulator=self.emulator.nfc_emulator,
1306            testcases=testcases,
1307            restore_original_frame_ordering=True,
1308        )
1309
1310        # Check that there are as many polling loop events as frames sent
1311        asserts.assert_equal(
1312            len(testcases), len(frames),
1313            _FAILED_MISSING_POLLING_FRAMES_MSG,
1314            get_frame_test_stats(frames=frames, testcases=testcases)
1315        )
1316
1317        issues = [
1318            {
1319                "index": idx,
1320                "expected": testcase.expected_data,
1321                "received": frame.data.hex()
1322            } for idx, (testcase, frame) in enumerate(zip(testcases, frames))
1323            if frame.data.hex() not in testcase.expected_data
1324        ]
1325
1326        for testcase, frame in zip(testcases, frames):
1327            if frame.data.hex() not in testcase.warning_data:
1328                continue
1329            _LOG.warning(
1330                f"Polling frame data variation '{frame.data.hex()}'" + \
1331                f" is accepted but not correct {testcase.success_data}"
1332            )
1333
1334        asserts.assert_equal(len(issues), 0, _FAILED_FRAME_DATA_INVALID, issues)
1335
1336    def teardown_test(self):
1337        if hasattr(self, 'emulator') and hasattr(self.emulator, 'nfc_emulator'):
1338            self.emulator.nfc_emulator.closeActivity()
1339            self.emulator.nfc_emulator.logInfo(
1340                "*** TEST END: " + self.current_test_info.name + " ***")
1341        if hasattr(self, 'pn532'):
1342            self.pn532.reset_buffers()
1343            self.pn532.mute()
1344        if hasattr(self, 'emulator'):
1345            param_list = [[self.emulator]]
1346            utils.concurrent_exec(lambda d: d.services.create_output_excerpts_all(
1347                self.current_test_info),
1348                                  param_list=param_list,
1349                                  raise_on_exception=True)
1350
1351    #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
1352    def test_single_non_payment_service_with_listen_tech_disabled(self):
1353        """Tests successful APDU exchange between non-payment service and
1354        reader does not proceed when Type-a listen tech is disabled.
1355
1356        Test Steps:
1357        1. Start emulator activity and set up non-payment HCE Service.
1358        2. Set listen tech to disabled on the emulator.
1359        3. Start PN532 and verify transaction does not proceed.
1360        5. Set listen tech to Type-A on the emulator.
1361        6. Start PN532 and verify APDU exchange between reader and emulator.
1362
1363        Verifies:
1364        1. Verifies that no APDU exchange occurs when the listen tech is disabled.
1365        2. Verifies a successful APDU exchange after re-enabling.
1366        """
1367        self._set_up_emulator(service_list=[_TRANSPORT_SERVICE_1],
1368                              expected_service=_TRANSPORT_SERVICE_1, is_payment=False)
1369        self.emulator.nfc_emulator.setListenTech(_NFC_LISTEN_OFF)
1370
1371        test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
1372            'ApduSuccess')
1373
1374        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator, _TRANSPORT_SERVICE_1)
1375        tag_detected, transacted = poll_and_transact(self.pn532, command_apdus[:1],
1376                                                     response_apdus[:1])
1377        asserts.assert_false(tag_detected, "Tag is detected unexpectedly!")
1378        asserts.assert_false(transacted, "Transaction is completed unexpectedly!")
1379
1380        # Set listen on
1381        self.emulator.nfc_emulator.setListenTech(_NFC_TECH_A_LISTEN_ON)
1382        tag_detected, transacted = poll_and_transact(self.pn532, command_apdus[:1],
1383                                                     response_apdus[:1])
1384        asserts.assert_true(tag_detected, _FAILED_TAG_MSG)
1385        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
1386
1387
1388    #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
1389    def test_single_non_payment_service_with_listen_tech_poll_tech_mismatch(self):
1390        """Tests successful APDU exchange between non-payment service and
1391        reader does not proceed when emulator listen tech mismatches reader poll tech.
1392
1393        Test Steps:
1394        1. Start emulator activity and set up non-payment HCE Service.
1395        2. Set listen tech to Type-F on the emulator.
1396        3. Start PN532 and verify transaction does not proceed.
1397        4. Set listen tech to Type-A on the emulator.
1398        6. Start PN532 and verify APDU exchange between reader and emulator.
1399
1400        Verifies:
1401        1. Verifies that no APDU exchange occurs when the listen tech mismatches with poll tech.
1402        2. Verifies a successful APDU exchange when no longer mismatched.
1403        """
1404        self._set_up_emulator(service_list=[_TRANSPORT_SERVICE_1],
1405                              expected_service=_TRANSPORT_SERVICE_1, is_payment=False)
1406        # Set listen to Type-F
1407        self.emulator.nfc_emulator.setListenTech(_NFC_TECH_F_LISTEN_ON)
1408
1409        command_apdus, response_apdus = get_apdus(self.emulator.nfc_emulator, _TRANSPORT_SERVICE_1)
1410        tag_detected, transacted = poll_and_transact(self.pn532, command_apdus[:1],
1411                                                     response_apdus[:1])
1412        asserts.assert_false(tag_detected, "Tag is detected unexpectedly!")
1413        asserts.assert_false(transacted, "Transaction is completed unexpectedly!")
1414
1415        # Set listen to Type-A
1416        self.emulator.nfc_emulator.setListenTech(_NFC_TECH_A_LISTEN_ON)
1417        tag_detected, transacted = poll_and_transact(self.pn532, command_apdus[:1], response_apdus[:1])
1418        asserts.assert_true(tag_detected, _FAILED_TAG_MSG)
1419        asserts.assert_true(transacted, _FAILED_TRANSACTION_MSG)
1420
1421if __name__ == '__main__':
1422    # Take test args
1423    if '--' in sys.argv:
1424        index = sys.argv.index('--')
1425        sys.argv = sys.argv[:1] + sys.argv[index + 1:]
1426    test_runner.main()
1427