/* * Copyright (C) 2021 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 "test_util.h" #include #include #include "chre/core/event_loop_manager.h" #include "chre/core/nanoapp.h" #include "chre/util/dynamic_vector.h" #include "chre/util/macros.h" #include "chre/util/memory.h" #include "chre_api/chre/version.h" #include "nanoapp/include/chre_nsl_internal/platform/shared/nanoapp_support_lib_dso.h" namespace chre { namespace { /** * List of chreNslNanoappInfo. * * Keep the chreNslNanoappInfo instances alive for the lifetime of the test * nanoapps. */ DynamicVector> gNanoappInfos; /** Registry of nanoapp by ID. */ std::unordered_map> nanoapps; /** * @return a pointer to a registered nanoapp or nullptr if the appId is not * registered. */ TestNanoapp *queryNanoapp(uint64_t appId) { return nanoapps.count(appId) == 0 ? nullptr : nanoapps[appId].get(); } /** * Nanoapp start. * * Invokes the start method of a registered TestNanoapp. */ bool start() { uint64_t id = chreGetAppId(); TestNanoapp *app = queryNanoapp(id); if (app == nullptr) { LOGE("[start] unregistered nanoapp 0x%016" PRIx64, id); return false; } return app->start(); } /** * Nanoapp handleEvent. * * Invokes the handleMethod method of a registered TestNanoapp. */ void handleEvent(uint32_t senderInstanceId, uint16_t eventType, const void *eventData) { uint64_t id = chreGetAppId(); TestNanoapp *app = queryNanoapp(id); if (app == nullptr) { LOGE("[handleEvent] unregistered nanoapp 0x%016" PRIx64, id); } else { app->handleEvent(senderInstanceId, eventType, eventData); } } /** * Nanoapp end. * * Invokes the end method of a registered TestNanoapp. */ void end() { uint64_t id = chreGetAppId(); TestNanoapp *app = queryNanoapp(id); if (app == nullptr) { LOGE("[end] unregistered nanoapp 0x%016" PRIx64, id); } else { app->end(); } } /** * Registers a test nanoapp. * * TestNanoapps are registered when they are loaded so that their entry point * methods can be called. */ void registerNanoapp(UniquePtr app) { if (nanoapps.count(app->id()) != 0) { LOGE("A nanoapp with the same id is already registered"); } else { nanoapps[app->id()] = std::move(app); } } /** * Unregisters a nanoapp. * * Calls the TestNanoapp destructor. */ void unregisterNanoapp(uint64_t appId) { if (nanoapps.erase(appId) == 0) { LOGE("The nanoapp is not registered"); } } } // namespace void unregisterAllTestNanoapps() { nanoapps.clear(); } UniquePtr createStaticNanoapp( const char *name, uint64_t appId, uint32_t appVersion, uint32_t appPerms, decltype(nanoappStart) *startFunc, decltype(nanoappHandleEvent) *handleEventFunc, decltype(nanoappEnd) *endFunc) { return createStaticNanoapp(CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION, name, appId, appVersion, appPerms, startFunc, handleEventFunc, endFunc); } UniquePtr createStaticNanoapp( uint8_t infoStructVersion, const char *name, uint64_t appId, uint32_t appVersion, uint32_t appPerms, decltype(nanoappStart) *startFunc, decltype(nanoappHandleEvent) *handleEventFunc, decltype(nanoappEnd) *endFunc) { auto nanoapp = MakeUnique(); auto nanoappInfo = MakeUnique(); chreNslNanoappInfo *appInfo = nanoappInfo.get(); gNanoappInfos.push_back(std::move(nanoappInfo)); appInfo->magic = CHRE_NSL_NANOAPP_INFO_MAGIC; appInfo->structMinorVersion = infoStructVersion; appInfo->targetApiVersion = CHRE_API_VERSION; appInfo->vendor = "Google"; appInfo->name = name; appInfo->isSystemNanoapp = true; appInfo->isTcmNanoapp = true; appInfo->appId = appId; appInfo->appVersion = appVersion; appInfo->entryPoints.start = startFunc; appInfo->entryPoints.handleEvent = handleEventFunc; appInfo->entryPoints.end = endFunc; appInfo->appVersionString = ""; appInfo->appPermissions = appPerms; EXPECT_FALSE(nanoapp.isNull()); nanoapp->loadStatic(appInfo); return nanoapp; } void deleteNanoappInfos() { gNanoappInfos.clear(); } bool defaultNanoappStart() { return true; } void defaultNanoappHandleEvent(uint32_t /*senderInstanceId*/, uint16_t /*eventType*/, const void * /*eventData*/) {} void defaultNanoappEnd() {} void loadNanoapp(const char *name, uint64_t appId, uint32_t appVersion, uint32_t appPerms, decltype(nanoappStart) *startFunc, decltype(nanoappHandleEvent) *handleEventFunc, decltype(nanoappEnd) *endFunc) { UniquePtr nanoapp = createStaticNanoapp( name, appId, appVersion, appPerms, startFunc, handleEventFunc, endFunc); EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::FinishLoadingNanoapp, std::move(nanoapp), testFinishLoadingNanoappCallback); TestEventQueueSingleton::get()->waitForEvent( CHRE_EVENT_SIMULATION_TEST_NANOAPP_LOADED); } uint64_t loadNanoapp(UniquePtr app) { TestNanoapp *pApp = app.get(); registerNanoapp(std::move(app)); loadNanoapp(pApp->name(), pApp->id(), pApp->version(), pApp->perms(), &start, &handleEvent, &end); return pApp->id(); } void sendEventToNanoapp(uint64_t appId, uint16_t eventType) { uint16_t instanceId; if (EventLoopManagerSingleton::get() ->getEventLoop() .findNanoappInstanceIdByAppId(appId, &instanceId)) { auto event = memoryAlloc(); ASSERT_NE(event, nullptr); event->type = eventType; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_TEST_EVENT, static_cast(event), freeTestEventDataCallback, instanceId); } else { LOGE("No instance found for nanoapp id = 0x%016" PRIx64, appId); } } void unloadNanoapp(uint64_t appId) { uint64_t *ptr = memoryAlloc(); ASSERT_NE(ptr, nullptr); *ptr = appId; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::HandleUnloadNanoapp, ptr, testFinishUnloadingNanoappCallback); TestEventQueueSingleton::get()->waitForEvent( CHRE_EVENT_SIMULATION_TEST_NANOAPP_UNLOADED); unregisterNanoapp(appId); } void testFinishLoadingNanoappCallback(SystemCallbackType /* type */, UniquePtr &&nanoapp) { EventLoopManagerSingleton::get()->getEventLoop().startNanoapp(nanoapp); TestEventQueueSingleton::get()->pushEvent( CHRE_EVENT_SIMULATION_TEST_NANOAPP_LOADED); } void testFinishUnloadingNanoappCallback(uint16_t /* type */, void *data, void * /* extraData */) { EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop(); uint16_t instanceId = 0; uint64_t *appId = static_cast(data); eventLoop.findNanoappInstanceIdByAppId(*appId, &instanceId); eventLoop.unloadNanoapp(instanceId, true); memoryFree(data); TestEventQueueSingleton::get()->pushEvent( CHRE_EVENT_SIMULATION_TEST_NANOAPP_UNLOADED); } void freeTestEventDataCallback(uint16_t /*eventType*/, void *eventData) { auto testEvent = static_cast(eventData); memoryFree(testEvent->data); memoryFree(testEvent); } } // namespace chre