269 lines
9.5 KiB
C++
269 lines
9.5 KiB
C++
/*
|
|
* Copyright (C) 2019, 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 <cstdlib>
|
|
#include <ctime>
|
|
#include <iostream>
|
|
#include <numeric>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
#include <sys/epoll.h>
|
|
#include <sys/eventfd.h>
|
|
#include <unistd.h>
|
|
|
|
#include <android-base/properties.h>
|
|
#include <android-base/unique_fd.h>
|
|
#include <android/hardware/tests/lazy/1.1/ILazy.h>
|
|
#include <android/hardware/tests/lazy_cb/1.0/ILazyCb.h>
|
|
#include <android/hidl/manager/1.2/IServiceManager.h>
|
|
#include <cutils/native_handle.h>
|
|
#include <gtest/gtest.h>
|
|
#include <hidl-util/FqInstance.h>
|
|
#include <hidl/HidlSupport.h>
|
|
#include <hidl/HidlTransportSupport.h>
|
|
#include <hidl/HidlTransportUtils.h>
|
|
#include <hwbinder/IPCThreadState.h>
|
|
|
|
using ::android::FqInstance;
|
|
using ::android::sp;
|
|
using ::android::base::unique_fd;
|
|
using ::android::hardware::hidl_handle;
|
|
using ::android::hardware::hidl_string;
|
|
using ::android::hardware::hidl_vec;
|
|
using ::android::hardware::IPCThreadState;
|
|
using ::android::hardware::Return;
|
|
using ::android::hardware::tests::lazy::V1_1::ILazy;
|
|
using ::android::hardware::tests::lazy_cb::V1_0::ILazyCb;
|
|
using ::android::hidl::base::V1_0::IBase;
|
|
using ::android::hidl::manager::V1_2::IServiceManager;
|
|
|
|
static std::vector<FqInstance> gInstances;
|
|
|
|
sp<IBase> getHal(const FqInstance& instance) {
|
|
return ::android::hardware::details::getRawServiceInternal(instance.getFqName().string(),
|
|
instance.getInstance(),
|
|
true /*retry*/, false /*getStub*/);
|
|
}
|
|
|
|
class HidlLazyTestBase : public ::testing::Test {
|
|
protected:
|
|
static constexpr size_t SHUTDOWN_WAIT_TIME = 10;
|
|
sp<IServiceManager> manager;
|
|
|
|
void SetUp() override {
|
|
manager = IServiceManager::getService();
|
|
ASSERT_NE(manager, nullptr);
|
|
}
|
|
|
|
bool isServiceRunning(const FqInstance& instance) {
|
|
bool isRunning = false;
|
|
EXPECT_TRUE(manager->listByInterface(instance.getFqName().string(),
|
|
[&](const hidl_vec<hidl_string>& instanceNames) {
|
|
for (const hidl_string& name : instanceNames) {
|
|
if (name == instance.getInstance()) {
|
|
isRunning = true;
|
|
break;
|
|
}
|
|
}
|
|
})
|
|
.isOk());
|
|
return isRunning;
|
|
}
|
|
};
|
|
|
|
class HidlLazyTest : public HidlLazyTestBase {
|
|
protected:
|
|
void SetUp() override {
|
|
HidlLazyTestBase::SetUp();
|
|
for (const auto& instance : gInstances) {
|
|
ASSERT_FALSE(isServiceRunning(instance))
|
|
<< "Service '" << instance.string()
|
|
<< "' is already running. Please ensure this "
|
|
<< "service is implemented as a lazy HAL, then kill all "
|
|
<< "clients of this service and try again.";
|
|
}
|
|
}
|
|
|
|
void TearDown() override {
|
|
std::cout << "Waiting " << SHUTDOWN_WAIT_TIME << " seconds before checking that the "
|
|
<< "service has shut down." << std::endl;
|
|
IPCThreadState::self()->flushCommands();
|
|
int timeout_multiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
|
|
sleep(SHUTDOWN_WAIT_TIME * timeout_multiplier);
|
|
for (const auto& instance : gInstances) {
|
|
ASSERT_FALSE(isServiceRunning(instance))
|
|
<< "Service failed to shutdown " << instance.string();
|
|
}
|
|
}
|
|
};
|
|
|
|
class HidlLazyCbTest : public HidlLazyTestBase {
|
|
protected:
|
|
static constexpr size_t CALLBACK_SHUTDOWN_WAIT_TIME = 5;
|
|
};
|
|
|
|
static constexpr size_t NUM_IMMEDIATE_GET_UNGETS = 100;
|
|
TEST_F(HidlLazyTest, GetUnget) {
|
|
for (size_t i = 0; i < NUM_IMMEDIATE_GET_UNGETS; i++) {
|
|
IPCThreadState::self()->flushCommands();
|
|
for (const auto& instance : gInstances) {
|
|
sp<IBase> hal = getHal(instance);
|
|
ASSERT_NE(hal.get(), nullptr);
|
|
EXPECT_TRUE(hal->ping().isOk());
|
|
}
|
|
}
|
|
}
|
|
|
|
static std::vector<size_t> waitTimes(size_t numTimes, size_t maxWait) {
|
|
std::vector<size_t> times(numTimes);
|
|
for (size_t i = 0; i < numTimes; i++) {
|
|
times[i] = (size_t)(rand() % (maxWait + 1));
|
|
}
|
|
return times;
|
|
}
|
|
|
|
static void testWithTimes(const std::vector<size_t>& waitTimes, const FqInstance& instance) {
|
|
std::cout << "Note runtime expected from sleeps: "
|
|
<< std::accumulate(waitTimes.begin(), waitTimes.end(), 0) << " second(s)."
|
|
<< std::endl;
|
|
|
|
for (size_t sleepTime : waitTimes) {
|
|
IPCThreadState::self()->flushCommands();
|
|
std::cout << "Thread for " << instance.string() << " waiting " << sleepTime
|
|
<< " while not holding HAL." << std::endl;
|
|
int timeout_multiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
|
|
sleep(sleepTime * timeout_multiplier);
|
|
sp<IBase> hal = getHal(instance);
|
|
ASSERT_NE(hal.get(), nullptr);
|
|
ASSERT_TRUE(hal->ping().isOk());
|
|
}
|
|
}
|
|
|
|
static constexpr size_t NUM_TIMES_GET_UNGET = 5;
|
|
static constexpr size_t MAX_WAITING_DURATION = 10;
|
|
static constexpr size_t NUM_CONCURRENT_THREADS = 5;
|
|
TEST_F(HidlLazyTest, GetWithWaitConcurrent) {
|
|
std::vector<std::vector<size_t>> threadWaitTimes(NUM_CONCURRENT_THREADS);
|
|
|
|
for (size_t i = 0; i < threadWaitTimes.size(); i++) {
|
|
threadWaitTimes[i] = waitTimes(NUM_TIMES_GET_UNGET, MAX_WAITING_DURATION);
|
|
}
|
|
|
|
std::vector<std::thread> threads(NUM_CONCURRENT_THREADS);
|
|
for (size_t i = 0; i < threads.size(); i++) {
|
|
const FqInstance& instance = gInstances[i % gInstances.size()];
|
|
threads[i] = std::thread(testWithTimes, threadWaitTimes[i], instance);
|
|
}
|
|
|
|
for (auto& thread : threads) {
|
|
thread.join();
|
|
}
|
|
}
|
|
|
|
TEST_F(HidlLazyCbTest, ActiveServicesCallbackTest) {
|
|
const std::string fqInstanceName = "android.hardware.tests.lazy_cb@1.0::ILazyCb/default";
|
|
FqInstance fqInstance;
|
|
ASSERT_TRUE(fqInstance.setTo(fqInstanceName));
|
|
|
|
ASSERT_FALSE(isServiceRunning(fqInstance)) << "Lazy service already running.";
|
|
|
|
sp<IBase> hal = getHal(fqInstance);
|
|
ASSERT_NE(hal, nullptr);
|
|
|
|
sp<ILazyCb> lazyCb = ILazyCb::castFrom(hal);
|
|
ASSERT_NE(lazyCb, nullptr);
|
|
hal = nullptr;
|
|
|
|
int efd = eventfd(0, 0);
|
|
ASSERT_GE(efd, 0) << "Failed to create eventfd";
|
|
unique_fd uniqueEventFd(efd);
|
|
|
|
native_handle_t* h = native_handle_create(/* numFds */ 1, /* numInts */ 0);
|
|
h->data[0] = efd;
|
|
hidl_handle handle(h);
|
|
Return<bool> setEventFdRet = lazyCb->setEventFd(handle);
|
|
native_handle_delete(h);
|
|
ASSERT_TRUE(setEventFdRet.isOk());
|
|
ASSERT_TRUE(setEventFdRet);
|
|
|
|
lazyCb = nullptr;
|
|
|
|
IPCThreadState::self()->flushCommands();
|
|
|
|
std::cout << "Waiting " << SHUTDOWN_WAIT_TIME << " seconds for callback completion "
|
|
<< "notification." << std::endl;
|
|
|
|
int epollFd = epoll_create1(EPOLL_CLOEXEC);
|
|
ASSERT_GE(epollFd, 0) << "Failed to create epoll";
|
|
unique_fd epollUniqueFd(epollFd);
|
|
|
|
const int EPOLL_MAX_EVENTS = 1;
|
|
struct epoll_event event, events[EPOLL_MAX_EVENTS];
|
|
|
|
event.events = EPOLLIN;
|
|
event.data.fd = efd;
|
|
int rc = epoll_ctl(epollFd, EPOLL_CTL_ADD, efd, &event);
|
|
ASSERT_GE(rc, 0) << "Failed to add fd to epoll";
|
|
|
|
rc = TEMP_FAILURE_RETRY(
|
|
epoll_wait(epollFd, events, EPOLL_MAX_EVENTS, SHUTDOWN_WAIT_TIME * 1000));
|
|
ASSERT_NE(rc, 0) << "Service shutdown timeout";
|
|
ASSERT_GT(rc, 0) << "Error waiting for service shutdown notification";
|
|
|
|
eventfd_t counter;
|
|
rc = TEMP_FAILURE_RETRY(eventfd_read(uniqueEventFd.get(), &counter));
|
|
ASSERT_GE(rc, 0) << "Failed to get callback completion notification from service";
|
|
ASSERT_EQ(counter, 1);
|
|
|
|
std::cout << "Waiting " << CALLBACK_SHUTDOWN_WAIT_TIME
|
|
<< " seconds before checking whether the "
|
|
<< "service is still running." << std::endl;
|
|
|
|
int timeout_multiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
|
|
sleep(CALLBACK_SHUTDOWN_WAIT_TIME * timeout_multiplier);
|
|
|
|
ASSERT_FALSE(isServiceRunning(fqInstance)) << "Service failed to shut down.";
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
|
|
srand(time(nullptr));
|
|
|
|
std::vector<std::string> fqInstances;
|
|
|
|
if (argc == 1) {
|
|
fqInstances.push_back("android.hardware.tests.lazy@1.0::ILazy/default1");
|
|
fqInstances.push_back("android.hardware.tests.lazy@1.0::ILazy/default2");
|
|
} else {
|
|
for (size_t arg = 1; arg < argc; arg++) {
|
|
fqInstances.push_back(argv[arg]);
|
|
}
|
|
}
|
|
|
|
for (const std::string& instance : fqInstances) {
|
|
FqInstance fqInstance;
|
|
if (!fqInstance.setTo(instance)) {
|
|
std::cerr << "Invalid fqinstance: " << instance << std::endl;
|
|
return 1;
|
|
}
|
|
gInstances.push_back(fqInstance);
|
|
}
|
|
|
|
return RUN_ALL_TESTS();
|
|
}
|