/* * Copyright (C) 2023 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 #include #include #include #include #include #include #include #include #include "chre/platform/shared/host_protocol_common.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "hal_client_manager.h" namespace android::hardware::contexthub::common::implementation { namespace { using aidl::android::hardware::contexthub::AsyncEventType; using aidl::android::hardware::contexthub::BnContextHubCallback; using aidl::android::hardware::contexthub::ContextHubMessage; using aidl::android::hardware::contexthub::MessageDeliveryStatus; using aidl::android::hardware::contexthub::NanoappInfo; using aidl::android::hardware::contexthub::NanSessionRequest; using ndk::ScopedAStatus; using ::testing::_; using ::testing::ByMove; using ::testing::Eq; using ::testing::IsEmpty; using ::testing::Optional; using ::testing::Return; using ::testing::SizeIs; using ::testing::UnorderedElementsAre; using HalClient = HalClientManager::Client; constexpr pid_t kSystemServerPid = 1000; // The uuid assigned to ContextHubService const std::string kSystemServerUuid = "9a17008d6bf1445a90116d21bd985b6c"; constexpr pid_t kVendorPid = 1001; const std::string kVendorUuid = "6e406b36cf4f4c0d8183db3708f45d8f"; const std::string kClientIdMappingFilePath = "./chre_hal_clients.json"; const std::string kClientName = "HalClientManagerTest"; class ContextHubCallbackForTest : public BnContextHubCallback { public: explicit ContextHubCallbackForTest(const std::string &uuid) { assert(uuid.length() == 32); // 2 digits for one bytes x 16 bytes for (int i = 0; i < 16; i++) { mUuid[i] = strtol(uuid.substr(i * 2, 2).c_str(), /* end_ptr= */ nullptr, /* base= */ 16); } ON_CALL(*this, handleContextHubAsyncEvent(_)) .WillByDefault(Return(ByMove(ScopedAStatus::ok()))); } ScopedAStatus handleNanoappInfo( const std::vector & /*appInfo*/) override { return ScopedAStatus::ok(); } ScopedAStatus handleContextHubMessage( const ContextHubMessage & /*message*/, const std::vector & /*msgContentPerms*/) override { return ScopedAStatus::ok(); } MOCK_METHOD(ScopedAStatus, handleContextHubAsyncEvent, (AsyncEventType event), (override)); // Called after loading/unloading a nanoapp. ScopedAStatus handleTransactionResult(int32_t /*transactionId*/, bool /*success*/) override { return ScopedAStatus::ok(); } ScopedAStatus handleNanSessionRequest( const NanSessionRequest & /* request */) override { return ScopedAStatus::ok(); } ScopedAStatus handleMessageDeliveryStatus( char16_t /* hostEndPointId */, const MessageDeliveryStatus & /* messageDeliveryStatus */) override { return ScopedAStatus::ok(); } ScopedAStatus getUuid(std::array *out_uuid) override { *out_uuid = mUuid; return ScopedAStatus::ok(); } ScopedAStatus getName(std::string *out_name) override { *out_name = kClientName; return ScopedAStatus::ok(); } private: const std::string kClientName = "HalClientManagerUnitTest"; std::array mUuid{}; }; class HalClientManagerForTest : public HalClientManager { public: HalClientManagerForTest( DeadClientUnlinker deadClientUnlinker, const std::string &clientIdMappingFilePath, const std::unordered_set &reservedClientIds = {}) : HalClientManager(std::move(deadClientUnlinker), clientIdMappingFilePath, reservedClientIds) {} const std::vector getClients() { return mClients; } bool createClientForTest(const std::string &uuid, pid_t pid) { // No need to hold the lock during a unit test which is single-threaded std::shared_ptr callback = ContextHubCallbackForTest::make( kSystemServerUuid); return createClient(uuid, pid, callback, /* deathRecipientCookie= */ nullptr); } HalClientId getNextClientId() { return mNextClientId; } static int64_t getTransactionTimeoutSeconds() { return kTransactionTimeoutThresholdMs / 1000; } static const char *getClientIdTag() { return kJsonClientId; } static const char *getUuidTag() { return kJsonUuid; } static const char *getNameTag() { return kJsonName; } }; class HalClientManagerTest : public ::testing::Test { protected: void SetUp() override { // Clears out the mapping file content std::ofstream file(kClientIdMappingFilePath); ASSERT_TRUE(file.good()); } void TearDown() override {} }; auto mockDeadClientUnlinker = [](const std::shared_ptr & /*callback*/, void * /*deathRecipientCookie*/) { return true; }; std::unique_ptr createLoadTransaction( uint32_t transactionId) { uint64_t appId = 0x476f6f676cabcdef; uint32_t appVersion = 2; uint32_t appFlags = 3; uint32_t targetApiVersion = 4; std::vector binary = {0xf0, 0xf1}; return std::make_unique( transactionId, appId, appVersion, appFlags, targetApiVersion, binary, /* fragmentSize= */ 2048); } TEST_F(HalClientManagerTest, ClientIdMappingFile) { HalClientId systemClientId = 100; { // Write systemClientId into the mapping file Json::Value mappings; Json::Value mapping; mapping[HalClientManagerForTest::getClientIdTag()] = systemClientId; mapping[HalClientManagerForTest::getUuidTag()] = kSystemServerUuid; mapping[HalClientManagerForTest::getNameTag()] = kClientName; mappings.append(mapping); Json::StreamWriterBuilder factory; std::unique_ptr const writer(factory.newStreamWriter()); std::ofstream fileStream(kClientIdMappingFilePath); writer->write(mappings, &fileStream); fileStream << std::endl; } auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr callback = ContextHubCallbackForTest::make( kSystemServerUuid); EXPECT_TRUE( halClientManager->registerCallback(kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr)); std::vector clients = halClientManager->getClients(); const HalClient &client = clients.front(); EXPECT_THAT(clients, SizeIs(1)); EXPECT_THAT(client.endpointIds, IsEmpty()); EXPECT_EQ(client.callback, callback); EXPECT_EQ(client.uuid, kSystemServerUuid); EXPECT_EQ(client.pid, kSystemServerPid); // The client id allocated should be the one specified in the mapping file EXPECT_EQ(client.clientId, systemClientId); } TEST_F(HalClientManagerTest, CallbackRegistryBasic) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr callback = ContextHubCallbackForTest::make( kSystemServerUuid); EXPECT_TRUE( halClientManager->registerCallback(kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr)); std::vector clients = halClientManager->getClients(); const HalClient &client = clients.front(); EXPECT_THAT(clients, SizeIs(1)); EXPECT_THAT(client.endpointIds, IsEmpty()); EXPECT_EQ(client.callback, callback); EXPECT_EQ(client.uuid, kSystemServerUuid); EXPECT_EQ(client.pid, kSystemServerPid); EXPECT_NE(client.clientId, ::chre::kHostClientIdUnspecified); } TEST_F(HalClientManagerTest, CallbackRegistryTwiceFromSameClient) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr callbackA = ContextHubCallbackForTest::make( kSystemServerUuid); std::shared_ptr callbackB = ContextHubCallbackForTest::make( kSystemServerUuid); EXPECT_TRUE( halClientManager->registerCallback(kSystemServerPid, callbackA, /* deathRecipientCookie= */ nullptr)); EXPECT_THAT(halClientManager->getClients(), SizeIs(1)); EXPECT_EQ(halClientManager->getClients().front().callback, callbackA); // Same client can override its callback EXPECT_TRUE( halClientManager->registerCallback(kSystemServerPid, callbackB, /* deathRecipientCookie= */ nullptr)); EXPECT_THAT(halClientManager->getClients(), SizeIs(1)); EXPECT_EQ(halClientManager->getClients().front().callback, callbackB); } TEST_F(HalClientManagerTest, CallbackRetrievalByEndpoint) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr systemCallback = ContextHubCallbackForTest::make( kSystemServerUuid); std::shared_ptr vendorCallback = ContextHubCallbackForTest::make(kVendorUuid); uint16_t vendorEndpointId = 1; uint16_t systemServerEndpointId = 1; // Register the callbacks and endpoint ids EXPECT_TRUE(halClientManager->registerCallback( kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); EXPECT_TRUE(halClientManager->registerEndpointId(kSystemServerPid, systemServerEndpointId)); EXPECT_TRUE(halClientManager->registerCallback( kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); EXPECT_TRUE( halClientManager->registerEndpointId(kVendorPid, vendorEndpointId)); // Though endpoint ids have the same value, they should be mutated before // getting sent to CHRE and mapped to different callbacks EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( kVendorPid, vendorEndpointId)); EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( kSystemServerPid, systemServerEndpointId)); EXPECT_EQ(halClientManager->getCallbackForEndpoint(vendorEndpointId), vendorCallback); EXPECT_EQ(halClientManager->getCallbackForEndpoint(systemServerEndpointId), systemCallback); } TEST_F(HalClientManagerTest, ClientCreation) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); int uuid = 1; int pid = 1; for (int i = 0; i < kMaxNumOfHalClients; i++, uuid++, pid++) { EXPECT_TRUE( halClientManager->createClientForTest(std::to_string(uuid), pid)); } // if max number of clients are reached no more client can be created EXPECT_FALSE( halClientManager->createClientForTest(std::to_string(uuid), pid)); // mNextClientId is reset to ::chre::kHostClientIdUnspecified when new client // is not accepted EXPECT_EQ(halClientManager->getNextClientId(), ::chre::kHostClientIdUnspecified); } TEST_F(HalClientManagerTest, ClientCreationWithReservedClientId) { std::unordered_set reservedClientIds{ ::chre::kHostClientIdUnspecified + 1, 64}; auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath, reservedClientIds); int uuid = 1; int pid = 1; for (int i = 0; i < kMaxNumOfHalClients - reservedClientIds.size(); i++, uuid++, pid++) { EXPECT_TRUE( halClientManager->createClientForTest(std::to_string(uuid), pid)); } // if max number of clients are reached no more client can be created EXPECT_FALSE( halClientManager->createClientForTest(std::to_string(uuid), pid)); // mNextClientId is reset to ::chre::kHostClientIdUnspecified when new client // is not accepted EXPECT_EQ(halClientManager->getNextClientId(), ::chre::kHostClientIdUnspecified); // Verify that every reserved client id is not used: for (const HalClient &client : halClientManager->getClients()) { EXPECT_EQ(reservedClientIds.find(client.clientId), reservedClientIds.end()); } } TEST_F(HalClientManagerTest, TransactionRegistryAndOverridden) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr callback = ContextHubCallbackForTest::make( kSystemServerUuid); EXPECT_TRUE(halClientManager->registerCallback( kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr)); EXPECT_TRUE(halClientManager->registerPendingLoadTransaction( kSystemServerPid, createLoadTransaction(/* transactionId= */ 1))); // Immediate transaction overridden is not allowed as each transaction is // given a certain amount of time to finish EXPECT_FALSE(halClientManager->registerPendingLoadTransaction( kSystemServerPid, createLoadTransaction(/* transactionId= */ 2))); // Wait until the transaction is timed out to override it std::this_thread::sleep_for(std::chrono::seconds( HalClientManagerForTest::getTransactionTimeoutSeconds())); EXPECT_TRUE(halClientManager->registerPendingLoadTransaction( kSystemServerPid, createLoadTransaction(/* transactionId= */ 3))); } TEST_F(HalClientManagerTest, TransactionRegistryLoadAndUnload) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr callback = ContextHubCallbackForTest::make( kSystemServerUuid); EXPECT_TRUE(halClientManager->registerCallback( kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr)); EXPECT_TRUE(halClientManager->registerPendingUnloadTransaction( kSystemServerPid, /* transactionId= */ 1, /* nanoappId= */ 2)); // Load and unload transaction can't coexist because unloading a nanoapp that // is being loaded can cause problems. EXPECT_FALSE(halClientManager->registerPendingLoadTransaction( kSystemServerPid, createLoadTransaction(/* transactionId= */ 2))); // Clears out the pending unload transaction to register a new one. halClientManager->resetPendingUnloadTransaction( halClientManager->getClientId(kSystemServerPid), /* transactionId= */ 1); EXPECT_TRUE(halClientManager->registerPendingLoadTransaction( kSystemServerPid, createLoadTransaction(/* transactionId= */ 2))); } TEST_F(HalClientManagerTest, EndpointRegistry) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr systemCallback = ContextHubCallbackForTest::make( kSystemServerUuid); std::shared_ptr vendorCallback = ContextHubCallbackForTest::make(kVendorUuid); EXPECT_THAT(halClientManager->getAllConnectedEndpoints(kSystemServerPid), Eq(std::nullopt)); halClientManager->registerCallback(kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr); halClientManager->registerCallback(kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr); std::vector clients = halClientManager->getClients(); EXPECT_THAT(clients, SizeIs(2)); EXPECT_THAT(halClientManager->getAllConnectedEndpoints(kSystemServerPid), Optional(IsEmpty())); EXPECT_THAT(halClientManager->getAllConnectedEndpoints(kVendorPid), Optional(IsEmpty())); // only system server can register endpoint ids > 63. EXPECT_TRUE(halClientManager->registerEndpointId(kSystemServerPid, /* endpointId= */ 64)); EXPECT_THAT(halClientManager->getAllConnectedEndpoints(kSystemServerPid), Optional(UnorderedElementsAre(64))); EXPECT_TRUE(halClientManager->registerEndpointId(kVendorPid, /*endpointId= */ 63)); EXPECT_FALSE(halClientManager->registerEndpointId(kVendorPid, /* endpointId= */ 64)); EXPECT_THAT(halClientManager->getAllConnectedEndpoints(kVendorPid), Optional(UnorderedElementsAre(63))); } TEST_F(HalClientManagerTest, EndpointIdMutationForVendorClient) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr vendorCallback = ContextHubCallbackForTest::make(kVendorUuid); std::shared_ptr systemCallback = ContextHubCallbackForTest::make( kSystemServerUuid); const uint16_t originalEndpointId = 10; // 0b1010; uint16_t mutatedEndpointId = originalEndpointId; // Register the system callback EXPECT_TRUE(halClientManager->registerCallback( kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); // Register the vendor callback EXPECT_TRUE(halClientManager->registerCallback( kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); // Mutate endpoint id from host to CHRE EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( kVendorPid, mutatedEndpointId)); HalClientId clientId = halClientManager->getClientId(kVendorPid); EXPECT_EQ(mutatedEndpointId, 0x8000 | clientId << 6 | originalEndpointId); // Mutate endpoint id from CHRE to Host EXPECT_EQ(halClientManager->convertToOriginalEndpointId(mutatedEndpointId), originalEndpointId); } TEST_F(HalClientManagerTest, EndpointIdMutationForSystemServer) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr callback = ContextHubCallbackForTest::make( kSystemServerUuid); const uint16_t originalEndpointId = 100; uint16_t mutatedEndpointId = originalEndpointId; // Register the callback EXPECT_TRUE(halClientManager->registerCallback( kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr)); // Endpoint id from the system server shouldn't be mutated EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( kSystemServerPid, mutatedEndpointId)); EXPECT_EQ(mutatedEndpointId, originalEndpointId); EXPECT_EQ(halClientManager->convertToOriginalEndpointId(mutatedEndpointId), originalEndpointId); } TEST_F(HalClientManagerTest, EndpointIdUnknownFromChre) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr vendorCallback = ContextHubCallbackForTest::make(kVendorUuid); std::shared_ptr systemCallback = ContextHubCallbackForTest::make( kSystemServerUuid); const HostEndpointId originalEndpointId = 0x10; // unregistered endpoint id HostEndpointId mutatedEndpointId = originalEndpointId; // Register the callback EXPECT_TRUE(halClientManager->registerCallback( kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); EXPECT_TRUE(halClientManager->registerCallback( kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); // As long as a client's callback is registered, hal_client_manager won't // block message exchanged from/to the client even if the endpoint id is // not registered. The enforcement of endpoint id registration is done on the // client side (contextHubService, library, etc.). EXPECT_TRUE(halClientManager->mutateEndpointIdFromHostIfNeeded( kVendorPid, mutatedEndpointId)); EXPECT_NE(mutatedEndpointId, originalEndpointId); EXPECT_EQ(halClientManager->convertToOriginalEndpointId(mutatedEndpointId), originalEndpointId); EXPECT_EQ(halClientManager->getCallbackForEndpoint(mutatedEndpointId), vendorCallback); } TEST_F(HalClientManagerTest, handleDeathClient) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr callback = ContextHubCallbackForTest::make( kSystemServerUuid); halClientManager->registerCallback(kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr); halClientManager->registerEndpointId(kSystemServerPid, /* endpointId= */ 10); halClientManager->handleClientDeath(kSystemServerPid); const std::vector &clients = halClientManager->getClients(); EXPECT_THAT(clients, SizeIs(1)); const HalClient &client = clients.front(); EXPECT_EQ(client.callback, nullptr); EXPECT_EQ(client.pid, HalClient::kPidUnset); EXPECT_EQ(client.uuid, kSystemServerUuid); EXPECT_NE(client.clientId, ::chre::kHostClientIdUnspecified); EXPECT_THAT(client.endpointIds, IsEmpty()); } TEST_F(HalClientManagerTest, handleChreRestart) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr vendorCallback = ContextHubCallbackForTest::make(kVendorUuid); std::shared_ptr systemCallback = ContextHubCallbackForTest::make( kSystemServerUuid); // Register the system callback EXPECT_TRUE(halClientManager->registerCallback( kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); // Register the vendor callback EXPECT_TRUE(halClientManager->registerCallback( kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); // Calls to clients' handleContextHubAsyncEvent should be postponed to HAL. EXPECT_CALL(*systemCallback, handleContextHubAsyncEvent(AsyncEventType::RESTARTED)) .Times(0); EXPECT_CALL(*vendorCallback, handleContextHubAsyncEvent(AsyncEventType::RESTARTED)) .Times(0); halClientManager->handleChreRestart(); } TEST_F(HalClientManagerTest, getAllConnectedCallbacks) { auto halClientManager = std::make_unique( mockDeadClientUnlinker, kClientIdMappingFilePath); std::shared_ptr vendorCallback = ContextHubCallbackForTest::make(kVendorUuid); std::shared_ptr systemCallback = ContextHubCallbackForTest::make( kSystemServerUuid); // Register the system callback EXPECT_TRUE(halClientManager->registerCallback( kSystemServerPid, systemCallback, /* deathRecipientCookie= */ nullptr)); // Register the vendor callback EXPECT_TRUE(halClientManager->registerCallback( kVendorPid, vendorCallback, /* deathRecipientCookie= */ nullptr)); EXPECT_THAT(halClientManager->getCallbacks(), UnorderedElementsAre(vendorCallback, systemCallback)); } } // namespace } // namespace android::hardware::contexthub::common::implementation