/* * Copyright (C) 2022 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. */ #include #include "chre/core/event_loop_manager.h" #include "chre/core/settings.h" #include "chre/platform/linux/pal_nan.h" #include "chre/platform/linux/pal_wifi.h" #include "chre/platform/log.h" #include "chre/util/system/napp_permissions.h" #include "chre_api/chre/event.h" #include "chre_api/chre/wifi.h" #include "gtest/gtest.h" #include "test_base.h" #include "test_event.h" #include "test_event_queue.h" #include "test_util.h" namespace chre { namespace { using namespace std::chrono_literals; CREATE_CHRE_TEST_EVENT(SCAN_REQUEST, 20); struct WifiAsyncData { const uint32_t *cookie; chreError errorCode; }; constexpr uint64_t kAppOneId = 0x0123456789000001; constexpr uint64_t kAppTwoId = 0x0123456789000002; class WifiScanRequestQueueTestBase : public TestBase { public: void SetUp() { TestBase::SetUp(); // Add delay to make sure the requests are queued. chrePalWifiDelayResponse(PalWifiAsyncRequestTypes::SCAN, /* milliseconds= */ 100ms); } void TearDown() { TestBase::TearDown(); chrePalWifiDelayResponse(PalWifiAsyncRequestTypes::SCAN, /* milliseconds= */ 0ms); } }; class WifiScanTestNanoapp : public TestNanoapp { public: WifiScanTestNanoapp() : TestNanoapp( TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} explicit WifiScanTestNanoapp(uint64_t id) : TestNanoapp(TestNanoappInfo{ .id = id, .perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_WIFI_ASYNC_RESULT: { auto *event = static_cast(eventData); TestEventQueueSingleton::get()->pushEvent( CHRE_EVENT_WIFI_ASYNC_RESULT, WifiAsyncData{ .cookie = static_cast(event->cookie), .errorCode = static_cast(event->errorCode)}); break; } case CHRE_EVENT_WIFI_SCAN_RESULT: { TestEventQueueSingleton::get()->pushEvent(CHRE_EVENT_WIFI_SCAN_RESULT); break; } case CHRE_EVENT_TEST_EVENT: { auto event = static_cast(eventData); switch (event->type) { case SCAN_REQUEST: bool success = false; if (mNextFreeCookieIndex < kMaxPendingCookie) { mCookies[mNextFreeCookieIndex] = *static_cast(event->data); success = chreWifiRequestScanAsyncDefault( &mCookies[mNextFreeCookieIndex]); mNextFreeCookieIndex++; } else { LOGE("Too many cookies passed from test body!"); } TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success); } } } } protected: static constexpr uint8_t kMaxPendingCookie = 10; uint32_t mCookies[kMaxPendingCookie]; uint8_t mNextFreeCookieIndex = 0; }; TEST_F(TestBase, WifiScanBasicSettingTest) { uint64_t appId = loadNanoapp(MakeUnique()); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, true /* enabled */); constexpr uint32_t firstCookie = 0x1010; bool success; WifiAsyncData wifiAsyncData; sendEventToNanoapp(appId, SCAN_REQUEST, firstCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &wifiAsyncData); EXPECT_EQ(wifiAsyncData.errorCode, CHRE_ERROR_NONE); EXPECT_EQ(*wifiAsyncData.cookie, firstCookie); waitForEvent(CHRE_EVENT_WIFI_SCAN_RESULT); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, false /* enabled */); constexpr uint32_t secondCookie = 0x2020; sendEventToNanoapp(appId, SCAN_REQUEST, secondCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); waitForEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, &wifiAsyncData); EXPECT_EQ(wifiAsyncData.errorCode, CHRE_ERROR_FUNCTION_DISABLED); EXPECT_EQ(*wifiAsyncData.cookie, secondCookie); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, true /* enabled */); unloadNanoapp(appId); } TEST_F(WifiScanRequestQueueTestBase, WifiQueuedScanSettingChangeTest) { CREATE_CHRE_TEST_EVENT(CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT, 1); CREATE_CHRE_TEST_EVENT(CONCURRENT_NANOAPP_READ_ASYNC_EVENT, 2); // Expecting to receive two event, one from each nanoapp. constexpr uint8_t kExpectedReceiveAsyncResultCount = 2; // receivedAsyncEventCount is shared across apps and must be static. // But we want it initialized each time the test is executed. static uint8_t receivedAsyncEventCount; receivedAsyncEventCount = 0; class WifiScanTestConcurrentNanoapp : public TestNanoapp { public: explicit WifiScanTestConcurrentNanoapp(uint64_t id) : TestNanoapp(TestNanoappInfo{ .id = id, .perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_WIFI_ASYNC_RESULT: { auto *event = static_cast(eventData); mReceivedAsyncResult = WifiAsyncData{ .cookie = static_cast(event->cookie), .errorCode = static_cast(event->errorCode)}; ++receivedAsyncEventCount; break; } case CHRE_EVENT_TEST_EVENT: { auto event = static_cast(eventData); bool success = false; switch (event->type) { case SCAN_REQUEST: mSentCookie = *static_cast(event->data); success = chreWifiRequestScanAsyncDefault(&(mSentCookie)); TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success); break; case CONCURRENT_NANOAPP_READ_ASYNC_EVENT: TestEventQueueSingleton::get()->pushEvent( CONCURRENT_NANOAPP_READ_ASYNC_EVENT, mReceivedAsyncResult); break; } } } if (receivedAsyncEventCount == kExpectedReceiveAsyncResultCount) { TestEventQueueSingleton::get()->pushEvent( CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT); } } protected: uint32_t mSentCookie; WifiAsyncData mReceivedAsyncResult; }; uint64_t appOneId = loadNanoapp(MakeUnique(kAppOneId)); uint64_t appTwoId = loadNanoapp(MakeUnique(kAppTwoId)); constexpr uint32_t appOneRequestCookie = 0x1010; constexpr uint32_t appTwoRequestCookie = 0x2020; bool success; sendEventToNanoapp(appOneId, SCAN_REQUEST, appOneRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); sendEventToNanoapp(appTwoId, SCAN_REQUEST, appTwoRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, false /* enabled */); // We need to make sure that each nanoapp has received one async result before // further analysis. waitForEvent(CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT); WifiAsyncData wifiAsyncData; sendEventToNanoapp(appOneId, CONCURRENT_NANOAPP_READ_ASYNC_EVENT); waitForEvent(CONCURRENT_NANOAPP_READ_ASYNC_EVENT, &wifiAsyncData); EXPECT_EQ(wifiAsyncData.errorCode, CHRE_ERROR_NONE); EXPECT_EQ(*wifiAsyncData.cookie, appOneRequestCookie); sendEventToNanoapp(appTwoId, CONCURRENT_NANOAPP_READ_ASYNC_EVENT); waitForEvent(CONCURRENT_NANOAPP_READ_ASYNC_EVENT, &wifiAsyncData); EXPECT_EQ(wifiAsyncData.errorCode, CHRE_ERROR_FUNCTION_DISABLED); EXPECT_EQ(*wifiAsyncData.cookie, appTwoRequestCookie); EventLoopManagerSingleton::get()->getSettingManager().postSettingChange( Setting::WIFI_AVAILABLE, true /* enabled */); unloadNanoapp(appOneId); unloadNanoapp(appTwoId); } TEST_F(WifiScanRequestQueueTestBase, WifiScanRejectRequestFromSameNanoapp) { CREATE_CHRE_TEST_EVENT(RECEIVED_ALL_EXPECTED_EVENTS, 1); CREATE_CHRE_TEST_EVENT(READ_ASYNC_EVENT, 2); static constexpr uint8_t kExpectedReceivedScanRequestCount = 2; class WifiScanTestBufferedAsyncResultNanoapp : public TestNanoapp { public: WifiScanTestBufferedAsyncResultNanoapp() : TestNanoapp( TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_WIFI_ASYNC_RESULT: { auto *event = static_cast(eventData); mReceivedAsyncResult = WifiAsyncData{ .cookie = static_cast(event->cookie), .errorCode = static_cast(event->errorCode)}; ++mReceivedAsyncEventCount; break; } case CHRE_EVENT_TEST_EVENT: { auto event = static_cast(eventData); bool success = false; switch (event->type) { case SCAN_REQUEST: if (mReceivedScanRequestCount >= kExpectedReceivedScanRequestCount) { LOGE("Asking too many scan request"); } else { mReceivedCookies[mReceivedScanRequestCount] = *static_cast(event->data); success = chreWifiRequestScanAsyncDefault( &(mReceivedCookies[mReceivedScanRequestCount])); ++mReceivedScanRequestCount; TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success); } break; case READ_ASYNC_EVENT: TestEventQueueSingleton::get()->pushEvent(READ_ASYNC_EVENT, mReceivedAsyncResult); break; } } } if (mReceivedAsyncEventCount == kExpectedReceivedAsyncResultCount && mReceivedScanRequestCount == kExpectedReceivedScanRequestCount) { TestEventQueueSingleton::get()->pushEvent(RECEIVED_ALL_EXPECTED_EVENTS); } } protected: // We are only expecting to receive one async result since the second // request is expected to fail. const uint8_t kExpectedReceivedAsyncResultCount = 1; uint8_t mReceivedAsyncEventCount = 0; uint8_t mReceivedScanRequestCount = 0; // We need to have two cookie storage to separate the two scan request. uint32_t mReceivedCookies[kExpectedReceivedScanRequestCount]; WifiAsyncData mReceivedAsyncResult; }; uint64_t appId = loadNanoapp(MakeUnique()); constexpr uint32_t kFirstRequestCookie = 0x1010; constexpr uint32_t kSecondRequestCookie = 0x2020; bool success; sendEventToNanoapp(appId, SCAN_REQUEST, kFirstRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); sendEventToNanoapp(appId, SCAN_REQUEST, kSecondRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_FALSE(success); // We need to make sure that the nanoapp has received one async result and did // two scan requests before further analysis. waitForEvent(RECEIVED_ALL_EXPECTED_EVENTS); WifiAsyncData wifiAsyncData; sendEventToNanoapp(appId, READ_ASYNC_EVENT); waitForEvent(READ_ASYNC_EVENT, &wifiAsyncData); EXPECT_EQ(wifiAsyncData.errorCode, CHRE_ERROR_NONE); EXPECT_EQ(*wifiAsyncData.cookie, kFirstRequestCookie); unloadNanoapp(appId); } TEST_F(WifiScanRequestQueueTestBase, WifiScanActiveScanFromDistinctNanoapps) { CREATE_CHRE_TEST_EVENT(CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT, 1); CREATE_CHRE_TEST_EVENT(CONCURRENT_NANOAPP_READ_COOKIE, 2); constexpr uint8_t kExpectedReceiveAsyncResultCount = 2; // receivedCookieCount is shared across apps and must be static. // But we want it initialized each time the test is executed. static uint8_t receivedCookieCount; receivedCookieCount = 0; class WifiScanTestConcurrentNanoapp : public TestNanoapp { public: explicit WifiScanTestConcurrentNanoapp(uint64_t id) : TestNanoapp(TestNanoappInfo{ .id = id, .perms = NanoappPermissions::CHRE_PERMS_WIFI}) {} void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_WIFI_ASYNC_RESULT: { auto *event = static_cast(eventData); if (event->errorCode == CHRE_ERROR_NONE) { mReceivedCookie = *static_cast(event->cookie); ++receivedCookieCount; } else { LOGE("Received failed async result"); } if (receivedCookieCount == kExpectedReceiveAsyncResultCount) { TestEventQueueSingleton::get()->pushEvent( CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT); } break; } case CHRE_EVENT_TEST_EVENT: { auto event = static_cast(eventData); bool success = false; switch (event->type) { case SCAN_REQUEST: mSentCookie = *static_cast(event->data); success = chreWifiRequestScanAsyncDefault(&(mSentCookie)); TestEventQueueSingleton::get()->pushEvent(SCAN_REQUEST, success); break; case CONCURRENT_NANOAPP_READ_COOKIE: TestEventQueueSingleton::get()->pushEvent( CONCURRENT_NANOAPP_READ_COOKIE, mReceivedCookie); break; } } } } protected: uint32_t mSentCookie; uint32_t mReceivedCookie; }; uint64_t appOneId = loadNanoapp(MakeUnique(kAppOneId)); uint64_t appTwoId = loadNanoapp(MakeUnique(kAppTwoId)); constexpr uint32_t kAppOneRequestCookie = 0x1010; constexpr uint32_t kAppTwoRequestCookie = 0x2020; bool success; sendEventToNanoapp(appOneId, SCAN_REQUEST, kAppOneRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); sendEventToNanoapp(appTwoId, SCAN_REQUEST, kAppTwoRequestCookie); waitForEvent(SCAN_REQUEST, &success); EXPECT_TRUE(success); waitForEvent(CONCURRENT_NANOAPP_RECEIVED_EXPECTED_ASYNC_EVENT_COUNT); uint32_t receivedCookie; sendEventToNanoapp(appOneId, CONCURRENT_NANOAPP_READ_COOKIE); waitForEvent(CONCURRENT_NANOAPP_READ_COOKIE, &receivedCookie); EXPECT_EQ(kAppOneRequestCookie, receivedCookie); sendEventToNanoapp(appTwoId, CONCURRENT_NANOAPP_READ_COOKIE); waitForEvent(CONCURRENT_NANOAPP_READ_COOKIE, &receivedCookie); EXPECT_EQ(kAppTwoRequestCookie, receivedCookie); unloadNanoapp(appOneId); unloadNanoapp(appTwoId); } } // namespace } // namespace chre