Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19d4a2e5 authored by Steve Pomeroy's avatar Steve Pomeroy Committed by Android (Google) Code Review
Browse files

Merge "Add VTS test for NFC observe mode" into main

parents c49bc476 64d876d9
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -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-V1-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",
    ],
}
+129 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
+86 −0
Original line number Diff line number Diff line
/*
 * 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;
};
+143 −0
Original line number Diff line number Diff line
/*
 * 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;
};
+223 −0
Original line number Diff line number Diff line
/*
 * 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 <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 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::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;
}