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