blob: 3e389a4e77b9a898759bbb30cea7b6439f57c174 [file] [log] [blame]
/*
* 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 "service_singleton_tests"
#include <mediautils/ServiceSingleton.h>
#include "BnServiceSingletonTest.h"
#include "aidl/BnServiceSingletonTest.h"
#include <audio_utils/RunRemote.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
using namespace android;
/**
* Service Singleton Test uses a worker process to spawn new binder services.
*
* A worker process is required since we cannot fork after registering
* with the binder driver.
*
* Test Process -> Worker_Process -> Service Process(1)
* -> Service Process(2)
* -> ....
*/
// Service implementation.
class ServiceSingletonTestCpp : public BnServiceSingletonTest {
public:
binder::Status inc(int32_t* _aidl_return) final {
*_aidl_return = ++mValue;
return binder::Status::ok();
}
std::atomic_int32_t mValue = 0;
};
// The service traits increment static atomic counters, which
// validates that the trait callbacks are invoked.
static std::atomic_int32_t sNewService = 0;
static std::atomic_int32_t sServiceDied = 0;
template <typename Service>
struct TestServiceTraits : public mediautils::DefaultServiceTraits<Service> {
static constexpr const char* getServiceName() { return ""; }
static constexpr void onNewService(const mediautils::InterfaceType<Service>&) {
++sNewService;
}
static constexpr void onServiceDied(const mediautils::InterfaceType<Service>&) {
++sServiceDied;
}
};
// Here we have an alternative set of service traits,
// used to validate that we can switch traits for the service singleton.
static std::atomic_int32_t sNewService2 = 0;
static std::atomic_int32_t sServiceDied2 = 0;
template <typename Service>
struct TestServiceTraits2 : public mediautils::DefaultServiceTraits<Service> {
static constexpr const char* getServiceName() { return ""; }
static constexpr void onNewService(const mediautils::InterfaceType<Service>&) {
++sNewService2;
}
static constexpr void onServiceDied(const mediautils::InterfaceType<Service>&) {
++sServiceDied2;
}
};
/*
* ServiceThreads run in a remote process.
*
* The WorkerThread is used to launch and kill the ServiceThread in a remote process.
*/
static void ServiceThread(audio_utils::RunRemote& runRemote) {
int c = runRemote.getc(); // requires any character to launch
auto service = sp<IServiceSingletonTest>::cast(sp<ServiceSingletonTestCpp>::make());
mediautils::addService(service);
ProcessState::self()->startThreadPool();
runRemote.putc(c); // echo character.
IPCThreadState::self()->joinThreadPool();
}
/*
* The WorkerThread is run in a remote process from the test. It communicates with
* the test process through pipes.
*/
static void WorkerThread(audio_utils::RunRemote& runRemote) {
std::shared_ptr<audio_utils::RunRemote> remoteService;
while (true) {
const int c = runRemote.getc();
switch (c) {
case 'a': // launch a new service.
// if the old service isn't destroyed, it will be destroyed here
// when the RunRemote is replaced.
remoteService = std::make_shared<audio_utils::RunRemote>(ServiceThread);
remoteService->run();
remoteService->putc('a'); // create service.
(void)remoteService->getc(); // ensure it is created.
runRemote.putc(c); // echo
break;
case 'b': // destroys the old service.
remoteService.reset(); // this kills the service.
runRemote.putc(c); // echo
break;
default: // respond that we don't know what happened!
runRemote.putc('?');
break;
}
}
}
// This is a monolithic test.
TEST(service_singleton_tests, one_and_only) {
std::atomic_int32_t listenerServiceCreated = 0;
std::atomic_int32_t listenerServiceDied = 0;
// initialize the service cache with a custom handler.
mediautils::initService<
IServiceSingletonTest, TestServiceTraits<IServiceSingletonTest>>({});
mediautils::initService<
aidl::IServiceSingletonTest, TestServiceTraits<aidl::IServiceSingletonTest>>({});
// start the worker thread that spawns the services.
auto remoteWorker = std::make_shared<audio_utils::RunRemote>(WorkerThread);
remoteWorker->run();
// now we are ready for binder.
ProcessState::self()->startThreadPool();
// check that our service isn't preexisting.
{
auto service = mediautils::checkServicePassThrough<IServiceSingletonTest>();
EXPECT_FALSE(service);
auto service2 = mediautils::checkServicePassThrough<aidl::IServiceSingletonTest>();
EXPECT_FALSE(service2);
}
EXPECT_EQ(0, sNewService);
EXPECT_EQ(0, sServiceDied);
{
auto service = mediautils::checkService<IServiceSingletonTest>();
EXPECT_FALSE(service);
auto service2 = mediautils::checkService<aidl::IServiceSingletonTest>();
EXPECT_FALSE(service2);
}
EXPECT_EQ(0, sNewService);
EXPECT_EQ(0, sServiceDied);
// getService will register a notification handler that fetches the
// service in the background.
{
auto service = mediautils::getService<IServiceSingletonTest>();
EXPECT_FALSE(service);
auto service2 = mediautils::getService<aidl::IServiceSingletonTest>();
EXPECT_FALSE(service2);
}
EXPECT_EQ(0, sNewService);
EXPECT_EQ(0, sServiceDied);
// now spawn the service.
remoteWorker->putc('a');
EXPECT_EQ('a', remoteWorker->getc());
sleep(1); // In the background, 2 services were fetched.
EXPECT_EQ(2, sNewService);
EXPECT_EQ(0, sServiceDied);
// we repeat the prior checks, but the service is cached now.
{
auto service = mediautils::checkServicePassThrough<IServiceSingletonTest>();
EXPECT_TRUE(service);
auto service2 = mediautils::checkServicePassThrough<aidl::IServiceSingletonTest>();
EXPECT_TRUE(service2);
}
EXPECT_EQ(2, sNewService);
EXPECT_EQ(0, sServiceDied);
{
auto service = mediautils::checkService<IServiceSingletonTest>();
EXPECT_TRUE(service);
auto service2 = mediautils::checkService<aidl::IServiceSingletonTest>();
EXPECT_TRUE(service2);
}
EXPECT_EQ(2, sNewService);
EXPECT_EQ(0, sServiceDied);
{
auto service = mediautils::getService<IServiceSingletonTest>();
EXPECT_TRUE(service);
auto service2 = mediautils::getService<aidl::IServiceSingletonTest>();
EXPECT_TRUE(service2);
}
EXPECT_EQ(2, sNewService);
EXPECT_EQ(0, sServiceDied);
// destroy the service.
remoteWorker->putc('b');
EXPECT_EQ('b', remoteWorker->getc());
sleep(1);
// We expect the died callbacks.
EXPECT_EQ(2, sNewService);
EXPECT_EQ(2, sServiceDied);
// we can also manually check whether there is a new service by
// requesting service notifications. This is outside of the service singleton
// traits.
auto handle1 = mediautils::requestServiceNotification<IServiceSingletonTest>(
[&](const sp<IServiceSingletonTest>&) { ++listenerServiceCreated; });
auto handle2 = mediautils::requestServiceNotification<aidl::IServiceSingletonTest>(
[&](const std::shared_ptr<aidl::IServiceSingletonTest>&) {
++listenerServiceCreated; });
// Spawn the service again.
remoteWorker->putc('a');
EXPECT_EQ('a', remoteWorker->getc());
sleep(1); // In the background, 2 services were fetched.
EXPECT_EQ(4, sNewService);
EXPECT_EQ(2, sServiceDied);
EXPECT_EQ(2, listenerServiceCreated); // our listener picked up the service creation.
std::shared_ptr<void> handle3, handle4;
std::shared_ptr<aidl::IServiceSingletonTest> keepAlive; // NDK Workaround!
{
auto service = mediautils::getService<IServiceSingletonTest>();
EXPECT_TRUE(service);
auto service2 = mediautils::getService<aidl::IServiceSingletonTest>();
EXPECT_TRUE(service2);
keepAlive = service2;
// we can also request our own death notifications (outside of the service traits).
handle3 = mediautils::requestDeathNotification(service, [&] { ++listenerServiceDied; });
EXPECT_TRUE(handle3);
handle4 = mediautils::requestDeathNotification(service2, [&] { ++listenerServiceDied; });
EXPECT_TRUE(handle4);
}
EXPECT_EQ(4, sNewService);
EXPECT_EQ(2, sServiceDied);
// destroy the service.
remoteWorker->putc('b');
EXPECT_EQ('b', remoteWorker->getc());
sleep(1);
// We expect the died callbacks.
EXPECT_EQ(4, sNewService);
EXPECT_EQ(4, sServiceDied);
EXPECT_EQ(2, listenerServiceCreated);
EXPECT_EQ(2, listenerServiceDied); // NDK Workaround - without keepAlive, this is 1.
// the death notification is invalidated without a
// pointer to the binder object.
keepAlive.reset();
// Cancel the singleton cache.
mediautils::skipService<IServiceSingletonTest>();
mediautils::skipService<aidl::IServiceSingletonTest>();
// Spawn the service again.
remoteWorker->putc('a');
EXPECT_EQ('a', remoteWorker->getc());
sleep(1);
// We expect no change from the service traits (service not cached).
EXPECT_EQ(4, sNewService);
EXPECT_EQ(4, sServiceDied);
EXPECT_EQ(4, listenerServiceCreated); // our listener picks it up.
{
// in default mode (kNull) a null is returned when the service is skipped and
// wait time is ignored.
const auto ref1 = std::chrono::steady_clock::now();
auto service = mediautils::getService<IServiceSingletonTest>(std::chrono::seconds(2));
EXPECT_FALSE(service);
const auto ref2 = std::chrono::steady_clock::now();
EXPECT_LT(ref2 - ref1, std::chrono::seconds(1));
auto service2 = mediautils::getService<aidl::IServiceSingletonTest>(
std::chrono::seconds(2));
EXPECT_FALSE(service2);
const auto ref3 = std::chrono::steady_clock::now();
EXPECT_LT(ref3 - ref2, std::chrono::seconds(1));
}
// Cancel the singleton cache but use wait mode.
mediautils::skipService<IServiceSingletonTest>(mediautils::SkipMode::kWait);
mediautils::skipService<aidl::IServiceSingletonTest>(mediautils::SkipMode::kWait);
{
// in wait mode, the timeouts are respected
const auto ref1 = std::chrono::steady_clock::now();
auto service = mediautils::getService<IServiceSingletonTest>(std::chrono::seconds(1));
EXPECT_FALSE(service);
const auto ref2 = std::chrono::steady_clock::now();
EXPECT_GT(ref2 - ref1, std::chrono::seconds(1));
auto service2 = mediautils::getService<aidl::IServiceSingletonTest>(
std::chrono::seconds(1));
EXPECT_FALSE(service2);
const auto ref3 = std::chrono::steady_clock::now();
EXPECT_GT(ref3 - ref2, std::chrono::seconds(1));
}
// remove service
remoteWorker->putc('b');
EXPECT_EQ('b', remoteWorker->getc());
sleep(1);
// We expect no change from the service traits (service not cached).
EXPECT_EQ(4, sNewService);
EXPECT_EQ(4, sServiceDied);
EXPECT_EQ(4, listenerServiceCreated);
EXPECT_EQ(2, listenerServiceDied); // binder died is associated with the actual handle.
// replace the service traits.
{
auto previous = mediautils::initService<
IServiceSingletonTest, TestServiceTraits2<IServiceSingletonTest>>({});
auto previous2 = mediautils::initService<
aidl::IServiceSingletonTest, TestServiceTraits2<aidl::IServiceSingletonTest>>({});
EXPECT_FALSE(previous);
EXPECT_FALSE(previous2);
}
// We expect no change with old counters.
EXPECT_EQ(4, sNewService);
EXPECT_EQ(4, sServiceDied);
EXPECT_EQ(0, sNewService2);
EXPECT_EQ(0, sServiceDied2);
{
auto service = mediautils::getService<IServiceSingletonTest>();
EXPECT_FALSE(service);
auto service2 = mediautils::getService<aidl::IServiceSingletonTest>();
EXPECT_FALSE(service2);
}
EXPECT_EQ(4, sNewService);
EXPECT_EQ(4, sServiceDied);
EXPECT_EQ(0, sNewService2);
EXPECT_EQ(0, sServiceDied2);
// Spawn the service again.
remoteWorker->putc('a');
EXPECT_EQ('a', remoteWorker->getc());
sleep(1);
EXPECT_EQ(4, sNewService); // old counters do not change.
EXPECT_EQ(4, sServiceDied);
EXPECT_EQ(2, sNewService2); // new counters change
EXPECT_EQ(0, sServiceDied2);
EXPECT_EQ(6, listenerServiceCreated); // listener associated with service name picks up info.
// get service pointers that will be made stale later.
auto stale_service = mediautils::getService<IServiceSingletonTest>();
EXPECT_TRUE(stale_service); // not stale yet.
auto stale_service2 = mediautils::getService<aidl::IServiceSingletonTest>();
EXPECT_TRUE(stale_service2); // not stale yet.
// Release the service.
remoteWorker->putc('b');
EXPECT_EQ('b', remoteWorker->getc());
sleep(1);
EXPECT_EQ(4, sNewService); // old counters do not change.
EXPECT_EQ(4, sServiceDied);
EXPECT_EQ(2, sNewService2); // new counters change
EXPECT_EQ(2, sServiceDied2);
// The service handles are now stale, verify that we can't register a death notification.
{
std::atomic_int32_t postDied = 0;
// we cannot register death notification so handles are null.
auto handle1 = mediautils::requestDeathNotification(stale_service, [&] { ++postDied; });
EXPECT_FALSE(handle1);
auto handle2= mediautils::requestDeathNotification(stale_service2, [&] { ++postDied; });
EXPECT_FALSE(handle2);
EXPECT_EQ(0, postDied); // no callbacks issued.
}
}