Merge changes from topics "nfc_aosp_main_merge", "nfc_aosp_main_merge_stage2" into main

* changes:
  Use parameterized test to disable test for devices without NFC
  Add VSR min API level
  Add VTS test for NFC observe mode
  [Hal] Add request/release control event in aidl interface.
This commit is contained in:
Treehugger Robot
2024-07-25 03:30:22 +00:00
committed by Gerrit Code Review
11 changed files with 672 additions and 1 deletions

View File

@@ -372,6 +372,7 @@
</hal>
<hal format="aidl" updatable-via-apex="true">
<name>android.hardware.nfc</name>
<version>1-2</version>
<interface>
<name>INfc</name>
<instance>default</instance>

View File

@@ -44,4 +44,5 @@ interface INfc {
int write(in byte[] data);
void setEnableVerboseLogging(in boolean enable);
boolean isVerboseLoggingEnabled();
android.hardware.nfc.NfcStatus controlGranted();
}

View File

@@ -40,4 +40,6 @@ enum NfcEvent {
PRE_DISCOVER_CPLT = 3,
HCI_NETWORK_RESET = 4,
ERROR = 5,
REQUEST_CONTROL = 6,
RELEASE_CONTROL = 7,
}

View File

@@ -140,4 +140,13 @@ interface INfc {
* @return true if verbose logging flag value is enabled, false if disabled.
*/
boolean isVerboseLoggingEnabled();
/**
* Requests control of NFCC to libnfc-nci.
* If an API request is sent when the framework has no control of NFCC, the request will be
* queued until the control is released from HAL.
* The control will be taken out of the framework for at most 2 seconds.
* @return NfcStatus::OK on success and NfcStatus::FAILED on error.
*/
NfcStatus controlGranted();
}

View File

@@ -50,4 +50,14 @@ enum NfcEvent {
* Error event to notify upper layer when there's an unknown error.
*/
ERROR = 5,
/**
* Request control event to notify upper layer when HAL
* request control of NFCC to libnfc-nci
*/
REQUEST_CONTROL = 6,
/**
* Release control event to notify upper layer when HAL
* release control of NFCC to libnfc-nci.
*/
RELEASE_CONTROL = 7,
}

View File

@@ -38,10 +38,55 @@ cc_test {
"libbinder_ndk",
],
static_libs: [
"android.hardware.nfc-V1-ndk",
"android.hardware.nfc-V2-ndk",
],
test_suites: [
"general-tests",
"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_options: {
vsr_min_shipping_api_level: 202404, // 2024Q2
},
test_suites: [
"vts",
],
}

View File

@@ -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 <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <errno.h>
#include <string.h>
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);
}
}

View File

@@ -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 <pthread.h>
#include <mutex>
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;
};

View File

@@ -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 <mutex>
#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;
};

View File

@@ -440,6 +440,16 @@ TEST_P(NfcAidl, CheckisVerboseLoggingEnabledAfterSetEnableVerboseLogging) {
EXPECT_TRUE(!enabled);
}
TEST_P(NfcAidl, CheckControlGrantedStatus) {
int interface_version;
EXPECT_TRUE(infc_->getInterfaceVersion(&interface_version).isOk());
if (interface_version > 1) {
NfcStatus status;
EXPECT_TRUE(infc_->controlGranted(&status).isOk());
EXPECT_EQ(status, NfcStatus::OK);
}
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NfcAidl);
INSTANTIATE_TEST_SUITE_P(Nfc, NfcAidl,
testing::ValuesIn(::android::getAidlHalInstanceNames(INfc::descriptor)),

View File

@@ -0,0 +1,235 @@
/*
* 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 <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/nfc/BnNfc.h>
#include <aidl/android/hardware/nfc/INfc.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
#include <chrono>
#include <future>
#include "NfcAdaptation.h"
#include "SyncEvent.h"
#include "nci_defs.h"
#include "nfa_api.h"
#include "nfa_ee_api.h"
using aidl::android::hardware::nfc::INfc;
using android::getAidlHalInstanceNames;
using android::PrintInstanceNameToString;
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<uint8_t>(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::TestWithParam<std::string> {
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_P(NfcBehaviorChanges, ObserveModeEnableDisable) {
tNFA_STATUS status = nfaObserveModeEnable(true);
ASSERT_EQ(status, NFA_STATUS_OK);
status = nfaObserveModeEnable(false);
ASSERT_EQ(status, NFA_STATUS_OK);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NfcBehaviorChanges);
INSTANTIATE_TEST_SUITE_P(Nfc, NfcBehaviorChanges,
testing::ValuesIn(::android::getAidlHalInstanceNames(INfc::descriptor)),
::android::PrintInstanceNameToString);
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;
}