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