blob: 73a885f77f5289c961c8e159e959970163f58b0b [file] [log] [blame]
#include <stdlib.h>
#include <array>
#include <chrono>
#include <fstream>
#include <thread>
#include <json/json.h>
#include <aidl/android/hardware/contexthub/BnContextHubCallback.h>
#include <aidl/android/hardware/contexthub/IContextHub.h>
#include <aidl/android/hardware/contexthub/NanoappBinary.h>
#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::NanoappInfo;
using aidl::android::hardware::contexthub::NanSessionRequest;
using ndk::ScopedAStatus;
using ::testing::_;
using ::testing::ByMove;
using ::testing::IsEmpty;
using ::testing::Return;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
using HalClient = HalClientManager::HalClient;
static constexpr pid_t kSystemServerPid = 1000;
// The uuid assigned to ContextHubService
static const std::string kSystemServerUuid = "9a17008d6bf1445a90116d21bd985b6c";
static constexpr pid_t kVendorPid = 1001;
static const std::string kVendorUuid = "6e406b36cf4f4c0d8183db3708f45d8f";
const std::string kClientIdMappingFilePath = "./chre_hal_clients.json";
class ContextHubCallbackForTest : public BnContextHubCallback {
public:
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<NanoappInfo> & /*appInfo*/) override {
return ScopedAStatus::ok();
}
ScopedAStatus handleContextHubMessage(
const ContextHubMessage & /*message*/,
const std::vector<std::string> & /*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 getUuid(std::array<uint8_t, 16> *out_uuid) override {
*out_uuid = mUuid;
return ScopedAStatus::ok();
}
private:
std::array<uint8_t, 16> mUuid{};
};
class HalClientManagerForTest : public HalClientManager {
public:
HalClientManagerForTest(DeadClientUnlinker deadClientUnlinker,
const std::string &clientIdMappingFilePath)
: HalClientManager(std::move(deadClientUnlinker),
clientIdMappingFilePath) {}
const std::vector<HalClient> getClients() {
return mClients;
}
static int64_t getTransactionTimeoutSeconds() {
return kTransactionTimeoutThresholdMs / 1000;
}
static const char *getClientIdTag() {
return kJsonClientId;
}
static const char *getUuidTag() {
return kJsonUuid;
}
};
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<IContextHubCallback> & /*callback*/,
void * /*deathRecipientCookie*/) { return true; };
std::unique_ptr<FragmentedLoadTransaction> createLoadTransaction(
uint32_t transactionId) {
uint64_t appId = 0x476f6f676cabcdef;
uint32_t appVersion = 2;
uint32_t appFlags = 3;
uint32_t targetApiVersion = 4;
std::vector<uint8_t> binary = {0xf0, 0xf1};
return std::make_unique<FragmentedLoadTransaction>(
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;
mappings.append(mapping);
Json::StreamWriterBuilder factory;
std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter());
std::ofstream fileStream(kClientIdMappingFilePath);
writer->write(mappings, &fileStream);
fileStream << std::endl;
}
auto halClientManager = std::make_unique<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> callback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
kSystemServerUuid);
EXPECT_TRUE(halClientManager->registerCallback(kSystemServerPid, callback,
/* cookie= */ nullptr));
std::vector<HalClient> 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<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> callback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
kSystemServerUuid);
EXPECT_TRUE(halClientManager->registerCallback(kSystemServerPid, callback,
/* cookie= */ nullptr));
std::vector<HalClient> 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<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> callbackA =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
kSystemServerUuid);
std::shared_ptr<ContextHubCallbackForTest> callbackB =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
kSystemServerUuid);
EXPECT_TRUE(halClientManager->registerCallback(kSystemServerPid, callbackA,
/* cookie= */ 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,
/* cookie= */ nullptr));
EXPECT_THAT(halClientManager->getClients(), SizeIs(1));
EXPECT_EQ(halClientManager->getClients().front().callback, callbackB);
}
TEST_F(HalClientManagerTest, CallbackRetrievalByEndpoint) {
auto halClientManager = std::make_unique<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> systemCallback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
kSystemServerUuid);
std::shared_ptr<ContextHubCallbackForTest> vendorCallback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(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, TransactionRegistryAndOverridden) {
auto halClientManager = std::make_unique<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> callback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
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<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> callback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
kSystemServerUuid);
EXPECT_TRUE(halClientManager->registerCallback(
kSystemServerPid, callback, /* deathRecipientCookie= */ nullptr));
EXPECT_TRUE(halClientManager->registerPendingUnloadTransaction(
kSystemServerPid, /* transactionId= */ 1));
// 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<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> systemCallback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
kSystemServerUuid);
std::shared_ptr<ContextHubCallbackForTest> vendorCallback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid);
halClientManager->registerCallback(kSystemServerPid, systemCallback,
/* cookie= */ nullptr);
halClientManager->registerCallback(kVendorPid, vendorCallback,
/* cookie= */ nullptr);
std::vector<HalClient> clients = halClientManager->getClients();
EXPECT_THAT(clients, SizeIs(2));
// only system server can register endpoint ids > 63.
EXPECT_TRUE(halClientManager->registerEndpointId(kSystemServerPid,
/* endpointId= */ 64));
EXPECT_TRUE(halClientManager->registerEndpointId(kVendorPid,
/*endpointId= */ 63));
EXPECT_FALSE(halClientManager->registerEndpointId(kVendorPid,
/* endpointId= */ 64));
}
TEST_F(HalClientManagerTest, EndpointIdMutationForVendorClient) {
auto halClientManager = std::make_unique<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> vendorCallback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid);
std::shared_ptr<ContextHubCallbackForTest> systemCallback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
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<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> callback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
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, handleDeathClient) {
auto halClientManager = std::make_unique<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> callback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
kSystemServerUuid);
halClientManager->registerCallback(kSystemServerPid, callback,
/* cookie= */ nullptr);
halClientManager->registerEndpointId(kSystemServerPid, /* endpointId= */ 10);
halClientManager->handleClientDeath(kSystemServerPid);
const std::vector<HalClient> &clients = halClientManager->getClients();
EXPECT_THAT(clients, SizeIs(1));
const HalClient &client = clients.front();
EXPECT_EQ(client.callback, nullptr);
EXPECT_EQ(client.pid, HalClient::PID_UNSET);
EXPECT_EQ(client.uuid, kSystemServerUuid);
EXPECT_NE(client.clientId, ::chre::kHostClientIdUnspecified);
EXPECT_THAT(client.endpointIds, IsEmpty());
}
TEST_F(HalClientManagerTest, handleChreRestartForConnectedClientsOnly) {
auto halClientManager = std::make_unique<HalClientManagerForTest>(
mockDeadClientUnlinker, kClientIdMappingFilePath);
std::shared_ptr<ContextHubCallbackForTest> vendorCallback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(kVendorUuid);
std::shared_ptr<ContextHubCallbackForTest> systemCallback =
ContextHubCallbackForTest::make<ContextHubCallbackForTest>(
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));
// Only connected clients' handleContextHubAsyncEvent should be called.
EXPECT_CALL(*systemCallback,
handleContextHubAsyncEvent(AsyncEventType::RESTARTED));
EXPECT_CALL(*vendorCallback,
handleContextHubAsyncEvent(AsyncEventType::RESTARTED))
.Times(0);
// Disconnect the vendor client and handle CHRE restart for the system server
halClientManager->handleClientDeath(kVendorPid);
halClientManager->handleChreRestart();
}
} // namespace
} // namespace android::hardware::contexthub::common::implementation