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

Commit bb7f928f authored by Steve Pomeroy's avatar Steve Pomeroy Committed by Roshan
Browse files

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
parent dad5092e
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-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",
    ],
}
+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;
}