From bb7f928fae05ba172a7e6816ee2c034db50402fb Mon Sep 17 00:00:00 2001 From: Steve Pomeroy Date: Wed, 6 Mar 2024 16:15:35 +0000 Subject: [PATCH] Add VTS test for NFC observe mode Test: this is only a test; manual run on a device Bug: 305979303 326470047 Merged-In: Idf4953e942bb5db8c2ee72779dfdf80ed4e224b2 Change-Id: Idf4953e942bb5db8c2ee72779dfdf80ed4e224b2 --- nfc/aidl/vts/functional/Android.bp | 42 ++++ nfc/aidl/vts/functional/CondVar.cpp | 129 ++++++++++ nfc/aidl/vts/functional/CondVar.h | 86 +++++++ nfc/aidl/vts/functional/SyncEvent.h | 143 +++++++++++ .../functional/VtsNfcBehaviorChangesTest.cpp | 223 ++++++++++++++++++ 5 files changed, 623 insertions(+) create mode 100644 nfc/aidl/vts/functional/CondVar.cpp create mode 100644 nfc/aidl/vts/functional/CondVar.h create mode 100644 nfc/aidl/vts/functional/SyncEvent.h create mode 100644 nfc/aidl/vts/functional/VtsNfcBehaviorChangesTest.cpp diff --git a/nfc/aidl/vts/functional/Android.bp b/nfc/aidl/vts/functional/Android.bp index 020086016d..b865255fb0 100644 --- a/nfc/aidl/vts/functional/Android.bp +++ b/nfc/aidl/vts/functional/Android.bp @@ -45,3 +45,45 @@ cc_test { "vts", ], } + +cc_test { + name: "VtsNfcBehaviorChangesTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsNfcBehaviorChangesTest.cpp", + "CondVar.cpp", + ], + include_dirs: [ + "system/nfc/src/gki/common", + "system/nfc/src/gki/ulinux", + "system/nfc/src/include", + "system/nfc/src/nfa/include", + "system/nfc/src/nfc/include", + "system/nfc/utils/include", + ], + shared_libs: [ + "libbinder", + "libbinder_ndk", + "libnativehelper", + "libstatssocket", + ], + static_libs: [ + "android.hardware.nfc-V2-ndk", + "android.hardware.nfc@1.0", + "android.hardware.nfc@1.1", + "android.hardware.nfc@1.2", + "android_nfc_flags_aconfig_c_lib", + "libnfc-nci", + "libnfc-nci_flags", + "libnfcutils", + "libstatslog_nfc", + "server_configurable_flags", + ], + require_root: true, + test_suites: [ + "vts", + ], +} diff --git a/nfc/aidl/vts/functional/CondVar.cpp b/nfc/aidl/vts/functional/CondVar.cpp new file mode 100644 index 0000000000..59e5b51f2b --- /dev/null +++ b/nfc/aidl/vts/functional/CondVar.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Encapsulate a condition variable for thread synchronization. + */ + +#include "CondVar.h" + +#include +#include +#include +#include + +using android::base::StringPrintf; + +/******************************************************************************* +** +** Function: CondVar +** +** Description: Initialize member variables. +** +** Returns: None. +** +*******************************************************************************/ +CondVar::CondVar() { + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + memset(&mCondition, 0, sizeof(mCondition)); + int const res = pthread_cond_init(&mCondition, &attr); + if (res) { + LOG(ERROR) << StringPrintf("CondVar::CondVar: fail init; error=0x%X", res); + } +} + +/******************************************************************************* +** +** Function: ~CondVar +** +** Description: Cleanup all resources. +** +** Returns: None. +** +*******************************************************************************/ +CondVar::~CondVar() { + int const res = pthread_cond_destroy(&mCondition); + if (res) { + LOG(ERROR) << StringPrintf("CondVar::~CondVar: fail destroy; error=0x%X", res); + } +} + +/******************************************************************************* +** +** Function: wait +** +** Description: Block the caller and wait for a condition. +** +** Returns: None. +** +*******************************************************************************/ +void CondVar::wait(std::mutex& mutex) { + int const res = pthread_cond_wait(&mCondition, mutex.native_handle()); + if (res) { + LOG(ERROR) << StringPrintf("CondVar::wait: fail wait; error=0x%X", res); + } +} + +/******************************************************************************* +** +** Function: wait +** +** Description: Block the caller and wait for a condition. +** millisec: Timeout in milliseconds. +** +** Returns: True if wait is successful; false if timeout occurs. +** +*******************************************************************************/ +bool CondVar::wait(std::mutex& mutex, long millisec) { + bool retVal = false; + struct timespec absoluteTime; + + if (clock_gettime(CLOCK_MONOTONIC, &absoluteTime) == -1) { + LOG(ERROR) << StringPrintf("CondVar::wait: fail get time; errno=0x%X", errno); + } else { + absoluteTime.tv_sec += millisec / 1000; + long ns = absoluteTime.tv_nsec + ((millisec % 1000) * 1000000); + if (ns > 1000000000) { + absoluteTime.tv_sec++; + absoluteTime.tv_nsec = ns - 1000000000; + } else + absoluteTime.tv_nsec = ns; + } + + int waitResult = pthread_cond_timedwait(&mCondition, mutex.native_handle(), &absoluteTime); + if ((waitResult != 0) && (waitResult != ETIMEDOUT)) + LOG(ERROR) << StringPrintf("CondVar::wait: fail timed wait; error=0x%X", waitResult); + retVal = (waitResult == 0); // waited successfully + return retVal; +} + +/******************************************************************************* +** +** Function: notifyOne +** +** Description: Unblock the waiting thread. +** +** Returns: None. +** +*******************************************************************************/ +void CondVar::notifyOne() { + int const res = pthread_cond_signal(&mCondition); + if (res) { + LOG(ERROR) << StringPrintf("CondVar::notifyOne: fail signal; error=0x%X", res); + } +} diff --git a/nfc/aidl/vts/functional/CondVar.h b/nfc/aidl/vts/functional/CondVar.h new file mode 100644 index 0000000000..5e0dcf77b4 --- /dev/null +++ b/nfc/aidl/vts/functional/CondVar.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Encapsulate a condition variable for thread synchronization. + */ + +#pragma once +#include + +#include + +class CondVar { + public: + /******************************************************************************* + ** + ** Function: CondVar + ** + ** Description: Initialize member variables. + ** + ** Returns: None. + ** + *******************************************************************************/ + CondVar(); + + /******************************************************************************* + ** + ** Function: ~CondVar + ** + ** Description: Cleanup all resources. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~CondVar(); + + /******************************************************************************* + ** + ** Function: wait + ** + ** Description: Block the caller and wait for a condition. + ** + ** Returns: None. + ** + *******************************************************************************/ + void wait(std::mutex& mutex); + + /******************************************************************************* + ** + ** Function: wait + ** + ** Description: Block the caller and wait for a condition. + ** millisec: Timeout in milliseconds. + ** + ** Returns: True if wait is successful; false if timeout occurs. + ** + *******************************************************************************/ + bool wait(std::mutex& mutex, long millisec); + + /******************************************************************************* + ** + ** Function: notifyOne + ** + ** Description: Unblock the waiting thread. + ** + ** Returns: None. + ** + *******************************************************************************/ + void notifyOne(); + + private: + pthread_cond_t mCondition; +}; diff --git a/nfc/aidl/vts/functional/SyncEvent.h b/nfc/aidl/vts/functional/SyncEvent.h new file mode 100644 index 0000000000..352a549aa1 --- /dev/null +++ b/nfc/aidl/vts/functional/SyncEvent.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Synchronize two or more threads using a condition variable and a mutex. + */ +#pragma once +#include + +#include "CondVar.h" + +class SyncEvent { + public: + /******************************************************************************* + ** + ** Function: ~SyncEvent + ** + ** Description: Cleanup all resources. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~SyncEvent() {} + + /******************************************************************************* + ** + ** Function: start + ** + ** Description: Start a synchronization operation. + ** + ** Returns: None. + ** + *******************************************************************************/ + void start() { mMutex.lock(); } + + /******************************************************************************* + ** + ** Function: wait + ** + ** Description: Block the thread and wait for the event to occur. + ** + ** Returns: None. + ** + *******************************************************************************/ + void wait() { mCondVar.wait(mMutex); } + + /******************************************************************************* + ** + ** Function: wait + ** + ** Description: Block the thread and wait for the event to occur. + ** millisec: Timeout in milliseconds. + ** + ** Returns: True if wait is successful; false if timeout occurs. + ** + *******************************************************************************/ + bool wait(long millisec) { + bool retVal = mCondVar.wait(mMutex, millisec); + return retVal; + } + + /******************************************************************************* + ** + ** Function: notifyOne + ** + ** Description: Notify a blocked thread that the event has occurred. + *Unblocks it. + ** + ** Returns: None. + ** + *******************************************************************************/ + void notifyOne() { mCondVar.notifyOne(); } + + /******************************************************************************* + ** + ** Function: end + ** + ** Description: End a synchronization operation. + ** + ** Returns: None. + ** + *******************************************************************************/ + void end() { mMutex.unlock(); } + + private: + CondVar mCondVar; + std::mutex mMutex; +}; + +/*****************************************************************************/ +/*****************************************************************************/ + +/***************************************************************************** +** +** Name: SyncEventGuard +** +** Description: Automatically start and end a synchronization event. +** +*****************************************************************************/ +class SyncEventGuard { + public: + /******************************************************************************* + ** + ** Function: SyncEventGuard + ** + ** Description: Start a synchronization operation. + ** + ** Returns: None. + ** + *******************************************************************************/ + SyncEventGuard(SyncEvent& event) : mEvent(event) { + event.start(); // automatically start operation + }; + + /******************************************************************************* + ** + ** Function: ~SyncEventGuard + ** + ** Description: End a synchronization operation. + ** + ** Returns: None. + ** + *******************************************************************************/ + ~SyncEventGuard() { + mEvent.end(); // automatically end operation + }; + + private: + SyncEvent& mEvent; +}; diff --git a/nfc/aidl/vts/functional/VtsNfcBehaviorChangesTest.cpp b/nfc/aidl/vts/functional/VtsNfcBehaviorChangesTest.cpp new file mode 100644 index 0000000000..0b73cc991a --- /dev/null +++ b/nfc/aidl/vts/functional/VtsNfcBehaviorChangesTest.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "nfc_behavior_changes_test" + +#include +#include +#include +#include + +#include +#include + +#include "NfcAdaptation.h" +#include "SyncEvent.h" +#include "nci_defs.h" +#include "nfa_api.h" +#include "nfa_ee_api.h" + +using android::base::StringPrintf; + +static SyncEvent sNfaEnableEvent; // event for NFA_Enable() +static SyncEvent sNfaVsCommand; // event for VS commands +static SyncEvent sNfaEnableDisablePollingEvent; +static SyncEvent sNfaPowerChangeEvent; +static bool sIsNfaEnabled; +static tNFA_STATUS sVSCmdStatus; + +static void nfaDeviceManagementCallback(uint8_t dmEvent, tNFA_DM_CBACK_DATA* eventData) { + LOG(DEBUG) << StringPrintf("%s: enter; event=0x%X", __func__, dmEvent); + + switch (dmEvent) { + case NFA_DM_ENABLE_EVT: /* Result of NFA_Enable */ + { + SyncEventGuard guard(sNfaEnableEvent); + LOG(DEBUG) << StringPrintf("%s: NFA_DM_ENABLE_EVT; status=0x%X", __func__, + eventData->status); + sIsNfaEnabled = eventData->status == NFA_STATUS_OK; + sNfaEnableEvent.notifyOne(); + } break; + + case NFA_DM_DISABLE_EVT: /* Result of NFA_Disable */ + { + SyncEventGuard guard(sNfaEnableEvent); + LOG(DEBUG) << StringPrintf("%s: NFA_DM_DISABLE_EVT; status=0x%X", __func__, + eventData->status); + sIsNfaEnabled = eventData->status == NFA_STATUS_OK; + sNfaEnableEvent.notifyOne(); + } break; + + case NFA_DM_PWR_MODE_CHANGE_EVT: { + SyncEventGuard guard(sNfaPowerChangeEvent); + LOG(DEBUG) << StringPrintf( + "%s: NFA_DM_PWR_MODE_CHANGE_EVT: status=0x%X, power_mode=0x%X", __func__, + eventData->status, eventData->power_mode.power_mode); + + sNfaPowerChangeEvent.notifyOne(); + + } break; + } +} + +static void nfaConnectionCallback(uint8_t connEvent, tNFA_CONN_EVT_DATA* eventData) { + LOG(DEBUG) << StringPrintf("%s: event= %u", __func__, connEvent); + + switch (connEvent) { + case NFA_LISTEN_DISABLED_EVT: { + SyncEventGuard guard(sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne(); + } break; + + case NFA_LISTEN_ENABLED_EVT: { + SyncEventGuard guard(sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne(); + } break; + + case NFA_RF_DISCOVERY_STARTED_EVT: // RF Discovery started + { + LOG(DEBUG) << StringPrintf("%s: NFA_RF_DISCOVERY_STARTED_EVT: status = %u", __func__, + eventData->status); + + SyncEventGuard guard(sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne(); + } break; + + case NFA_RF_DISCOVERY_STOPPED_EVT: // RF Discovery stopped event + { + LOG(DEBUG) << StringPrintf("%s: NFA_RF_DISCOVERY_STOPPED_EVT: status = %u", __func__, + eventData->status); + + SyncEventGuard guard(sNfaEnableDisablePollingEvent); + sNfaEnableDisablePollingEvent.notifyOne(); + } break; + } +} + +void static nfaVSCallback(uint8_t event, uint16_t /* param_len */, uint8_t* p_param) { + switch (event & NCI_OID_MASK) { + case NCI_MSG_PROP_ANDROID: { + uint8_t android_sub_opcode = p_param[3]; + switch (android_sub_opcode) { + case NCI_ANDROID_PASSIVE_OBSERVE: { + sVSCmdStatus = p_param[4]; + LOG(INFO) << StringPrintf("Observe mode RSP: status: %x", sVSCmdStatus); + SyncEventGuard guard(sNfaVsCommand); + sNfaVsCommand.notifyOne(); + } break; + case NCI_ANDROID_POLLING_FRAME_NTF: { + // TODO + } break; + default: + LOG(WARNING) << StringPrintf("Unknown Android sub opcode %x", + android_sub_opcode); + } + } break; + default: + break; + } +} + +/* + * Enable passive observe mode. + */ +tNFA_STATUS static nfaObserveModeEnable(bool enable) { + tNFA_STATUS status = NFA_STATUS_FAILED; + + status = NFA_StopRfDiscovery(); + if (status == NFA_STATUS_OK) { + if (!sNfaEnableDisablePollingEvent.wait(1000)) { + LOG(WARNING) << "Timeout waiting to disable NFC RF discovery"; + return NFA_STATUS_TIMEOUT; + } + } + + uint8_t cmd[] = {(NCI_MT_CMD << NCI_MT_SHIFT) | NCI_GID_PROP, NCI_MSG_PROP_ANDROID, + NCI_ANDROID_PASSIVE_OBSERVE_PARAM_SIZE, NCI_ANDROID_PASSIVE_OBSERVE, + static_cast(enable ? NCI_ANDROID_PASSIVE_OBSERVE_PARAM_ENABLE + : NCI_ANDROID_PASSIVE_OBSERVE_PARAM_DISABLE)}; + + status = NFA_SendRawVsCommand(sizeof(cmd), cmd, nfaVSCallback); + + if (status == NFA_STATUS_OK) { + if (!sNfaVsCommand.wait(1000)) { + LOG(WARNING) << "Timeout waiting for NFA VS command response"; + return NFA_STATUS_TIMEOUT; + } + } + + return status; +} + +class NfcBehaviorChanges : public testing::Test { + protected: + void SetUp() override { + tNFA_STATUS status = NFA_STATUS_OK; + + sIsNfaEnabled = false; + sVSCmdStatus = NFA_STATUS_OK; + + NfcAdaptation& theInstance = NfcAdaptation::GetInstance(); + theInstance.Initialize(); // start GKI, NCI task, NFC task + + { + SyncEventGuard guard(sNfaEnableEvent); + tHAL_NFC_ENTRY* halFuncEntries = theInstance.GetHalEntryFuncs(); + + NFA_Init(halFuncEntries); + + status = NFA_Enable(nfaDeviceManagementCallback, nfaConnectionCallback); + ASSERT_EQ(status, NFA_STATUS_OK); + + // wait for NFA command to finish + ASSERT_TRUE(sNfaEnableEvent.wait(1000)) + << "Timeout waiting for NFA command on NFA_Enable"; + } + + ASSERT_TRUE(sIsNfaEnabled) << "Could not initialize NFC controller"; + + status = NFA_StartRfDiscovery(); + ASSERT_EQ(status, NFA_STATUS_OK); + ASSERT_TRUE(sNfaEnableDisablePollingEvent.wait(1000)) << "Timeout starting RF discovery"; + } +}; + +/* + * ObserveModeEnable: + * Attempts to enable observe mode. Does not test Observe Mode functionality, + * but simply verifies that the enable command responds successfully. + * + * @VsrTest = GMS-VSR-3.2.8-001 + */ +TEST_F(NfcBehaviorChanges, ObserveModeEnableDisable) { + tNFA_STATUS status = nfaObserveModeEnable(true); + ASSERT_EQ(status, NFA_STATUS_OK); + + status = nfaObserveModeEnable(false); + ASSERT_EQ(status, NFA_STATUS_OK); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + ABinderProcess_startThreadPool(); + std::system("/system/bin/svc nfc disable"); /* Turn off NFC service */ + sleep(5); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + std::system("/system/bin/svc nfc enable"); /* Turn on NFC service */ + sleep(5); + return status; +}