blob: 70de9dd9e0ee15fffbda0573d05c5aeedac6e057 [file] [log] [blame]
/*
* Copyright (C) 2016 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 <gtest/gtest.h>
#ifndef GTEST_IS_THREADSAFE
#error "GTest did not detect pthread library."
#endif
#include <aidl/android/fmq/test/FixedParcelable.h>
#include <aidl/android/fmq/test/FixedUnion.h>
#include <aidl/android/fmq/test/ITestAidlMsgQ.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <android/hardware/tests/msgq/1.0/ITestMsgQ.h>
#include <fmq/AidlMessageQueue.h>
#include <fmq/EventFlag.h>
#include <fmq/MessageQueue.h>
#include <hidl/ServiceManagement.h>
// libutils:
using android::OK;
using android::sp;
using android::status_t;
// generated
using ::aidl::android::fmq::test::EventFlagBits;
using ::aidl::android::fmq::test::FixedParcelable;
using ::aidl::android::fmq::test::FixedUnion;
using ::aidl::android::fmq::test::ITestAidlMsgQ;
using android::hardware::tests::msgq::V1_0::ITestMsgQ;
static_assert(static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL) ==
static_cast<uint32_t>(EventFlagBits::FMQ_NOT_FULL),
"The AIDL and HIDL test interfaces must use the same values!");
static_assert(static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY) ==
static_cast<uint32_t>(EventFlagBits::FMQ_NOT_EMPTY),
"The AIDL and HIDL test interfaces must use the same values!");
// libhidl
using android::hardware::isHidlSupported;
using android::hardware::kSynchronizedReadWrite;
using android::hardware::kUnsynchronizedWrite;
using android::hardware::MessageQueue;
using android::hardware::MQDescriptorSync;
using android::hardware::MQDescriptorUnsync;
using android::hardware::details::waitForHwService;
using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using aidl::android::hardware::common::fmq::UnsynchronizedWrite;
using android::hardware::kSynchronizedReadWrite;
using android::hardware::kUnsynchronizedWrite;
typedef android::AidlMessageQueue<int32_t, SynchronizedReadWrite> AidlMessageQueueSync;
typedef android::AidlMessageQueue<int32_t, UnsynchronizedWrite> AidlMessageQueueUnsync;
typedef android::hardware::MessageQueue<int32_t, kSynchronizedReadWrite> MessageQueueSync;
typedef android::hardware::MessageQueue<int32_t, kUnsynchronizedWrite> MessageQueueUnsync;
static const std::string kServiceName = "BnTestAidlMsgQ";
static const size_t kPageSize = getpagesize();
static const size_t kNumElementsInSyncQueue = (kPageSize - 16) / sizeof(int32_t);
enum class SetupType {
SINGLE_FD,
DOUBLE_FD,
};
template <typename T, SetupType setupType>
class TestParamTypes {
public:
typedef T MQType;
static constexpr bool UserFd = setupType == SetupType::DOUBLE_FD;
};
// Run everything on both the AIDL and HIDL versions with one and two FDs
typedef ::testing::Types<TestParamTypes<AidlMessageQueueSync, SetupType::SINGLE_FD>,
TestParamTypes<MessageQueueSync, SetupType::SINGLE_FD>,
TestParamTypes<AidlMessageQueueSync, SetupType::DOUBLE_FD>,
TestParamTypes<MessageQueueSync, SetupType::DOUBLE_FD>>
SyncTypes;
typedef ::testing::Types<TestParamTypes<AidlMessageQueueUnsync, SetupType::SINGLE_FD>,
TestParamTypes<MessageQueueUnsync, SetupType::SINGLE_FD>,
TestParamTypes<AidlMessageQueueUnsync, SetupType::DOUBLE_FD>,
TestParamTypes<MessageQueueUnsync, SetupType::DOUBLE_FD>>
UnsyncTypes;
template <typename T>
class ClientSyncTestBase : public ::testing::Test {};
// Specialize for AIDL
template <>
class ClientSyncTestBase<AidlMessageQueueSync> : public ::testing::Test {
protected:
static std::shared_ptr<ITestAidlMsgQ> waitGetTestService() {
const std::string instance = std::string() + ITestAidlMsgQ::descriptor + "/default";
ndk::SpAIBinder binder(AServiceManager_getService(instance.c_str()));
CHECK(nullptr != binder);
return ITestAidlMsgQ::fromBinder(binder);
}
bool configureFmqSyncReadWrite(AidlMessageQueueSync* mq) {
bool result = false;
auto ret = mService->configureFmqSyncReadWrite(mq->dupeDesc(), &result);
return result && ret.isOk();
}
bool requestReadFmqSync(size_t dataLen) {
bool result = false;
auto ret = mService->requestReadFmqSync(dataLen, &result);
return result && ret.isOk();
}
bool requestWriteFmqSync(size_t dataLen) {
bool result = false;
auto ret = mService->requestWriteFmqSync(dataLen, &result);
return result && ret.isOk();
}
std::shared_ptr<ITestAidlMsgQ> mService;
};
// Specialize for HIDL
template <>
class ClientSyncTestBase<MessageQueueSync> : public ::testing::Test {
protected:
static sp<ITestMsgQ> waitGetTestService() {
if (isHidlSupported()) {
android::hardware::details::setTrebleTestingOverride(true);
// waitForHwService is required because ITestMsgQ is not in manifest.xml.
// "Real" HALs shouldn't be doing this.
waitForHwService(ITestMsgQ::descriptor, "default");
sp<ITestMsgQ> service = ITestMsgQ::getService();
CHECK(nullptr != service);
return service;
} else {
return nullptr;
}
}
bool configureFmqSyncReadWrite(MessageQueueSync* mq) {
auto ret = mService->configureFmqSyncReadWrite(*mq->getDesc());
return ret && ret.isOk();
}
bool requestReadFmqSync(size_t dataLen) {
auto ret = mService->requestReadFmqSync(dataLen);
return ret && ret.isOk();
}
bool requestWriteFmqSync(size_t dataLen) {
auto ret = mService->requestWriteFmqSync(dataLen);
return ret && ret.isOk();
}
sp<ITestMsgQ> mService;
};
template <typename T>
class ClientUnsyncTestBase : public ::testing::Test {};
// Specialize for AIDL
template <>
class ClientUnsyncTestBase<AidlMessageQueueUnsync> : public ::testing::Test {
protected:
static std::shared_ptr<ITestAidlMsgQ> waitGetTestService() {
const std::string instance = std::string() + ITestAidlMsgQ::descriptor + "/default";
ndk::SpAIBinder binder(AServiceManager_getService(instance.c_str()));
CHECK(nullptr != binder);
return ITestAidlMsgQ::fromBinder(binder);
}
bool getFmqUnsyncWrite(bool configureFmq, bool userFd, std::shared_ptr<ITestAidlMsgQ> service,
AidlMessageQueueUnsync** queue) {
bool result = false;
aidl::android::hardware::common::fmq::MQDescriptor<int32_t, UnsynchronizedWrite> desc;
auto ret = service->getFmqUnsyncWrite(configureFmq, userFd, &desc, &result);
*queue = new (std::nothrow) AidlMessageQueueUnsync(desc, false);
return result && ret.isOk();
}
std::shared_ptr<ITestAidlMsgQ> getQueue(AidlMessageQueueUnsync** fmq, bool setupQueue,
bool userFd) {
std::shared_ptr<ITestAidlMsgQ> service = waitGetTestService();
if (service == nullptr) return nullptr;
getFmqUnsyncWrite(setupQueue, userFd, service, fmq);
return service;
}
bool requestReadFmqUnsync(size_t dataLen, std::shared_ptr<ITestAidlMsgQ> service) {
bool result = false;
auto ret = service->requestReadFmqUnsync(dataLen, &result);
return result && ret.isOk();
}
bool requestWriteFmqUnsync(size_t dataLen, std::shared_ptr<ITestAidlMsgQ> service) {
bool result = false;
auto ret = service->requestWriteFmqUnsync(dataLen, &result);
return result && ret.isOk();
}
AidlMessageQueueUnsync* newQueue() {
if (mQueue->isValid())
return new (std::nothrow) AidlMessageQueueUnsync(mQueue->dupeDesc(), false);
else
return nullptr;
}
std::shared_ptr<ITestAidlMsgQ> mService;
AidlMessageQueueUnsync* mQueue = nullptr;
};
// Specialize for HIDL
template <>
class ClientUnsyncTestBase<MessageQueueUnsync> : public ::testing::Test {
protected:
static sp<ITestMsgQ> waitGetTestService() {
if (isHidlSupported()) {
android::hardware::details::setTrebleTestingOverride(true);
// waitForHwService is required because ITestMsgQ is not in manifest.xml.
// "Real" HALs shouldn't be doing this.
waitForHwService(ITestMsgQ::descriptor, "default");
sp<ITestMsgQ> service = ITestMsgQ::getService();
CHECK(nullptr != service);
return service;
} else {
return nullptr;
}
}
bool getFmqUnsyncWrite(bool configureFmq, bool userFd, sp<ITestMsgQ> service,
MessageQueueUnsync** queue) {
if (!service) {
return false;
}
service->getFmqUnsyncWrite(configureFmq, userFd,
[queue](bool ret, const MQDescriptorUnsync<int32_t>& in) {
ASSERT_TRUE(ret);
*queue = new (std::nothrow) MessageQueueUnsync(in, false);
});
return true;
}
sp<ITestMsgQ> getQueue(MessageQueueUnsync** fmq, bool setupQueue, bool userFd) {
sp<ITestMsgQ> service = waitGetTestService();
if (service == nullptr) return nullptr;
getFmqUnsyncWrite(setupQueue, userFd, service, fmq);
return service;
}
bool requestReadFmqUnsync(size_t dataLen, sp<ITestMsgQ> service) {
auto ret = service->requestReadFmqUnsync(dataLen);
return ret && ret.isOk();
}
bool requestWriteFmqUnsync(size_t dataLen, sp<ITestMsgQ> service) {
auto ret = service->requestWriteFmqUnsync(dataLen);
return ret && ret.isOk();
}
MessageQueueUnsync* newQueue() {
return new (std::nothrow) MessageQueueUnsync(*mQueue->getDesc(), false);
}
sp<ITestMsgQ> mService;
MessageQueueUnsync* mQueue = nullptr;
};
TYPED_TEST_CASE(UnsynchronizedWriteClientMultiProcess, UnsyncTypes);
template <typename T>
class UnsynchronizedWriteClientMultiProcess : public ClientUnsyncTestBase<typename T::MQType> {};
TYPED_TEST_CASE(SynchronizedReadWriteClient, SyncTypes);
template <typename T>
class SynchronizedReadWriteClient : public ClientSyncTestBase<typename T::MQType> {
protected:
virtual void TearDown() {
delete mQueue;
}
virtual void SetUp() {
this->mService = this->waitGetTestService();
if (this->mService == nullptr) GTEST_SKIP() << "HIDL is not supported";
ASSERT_TRUE(this->mService->isRemote());
static constexpr size_t kSyncElementSizeBytes = sizeof(int32_t);
android::base::unique_fd ringbufferFd;
if (T::UserFd) {
ringbufferFd.reset(::ashmem_create_region(
"SyncReadWrite", kNumElementsInSyncQueue * kSyncElementSizeBytes));
}
// create a queue on the client side
mQueue = new (std::nothrow) typename T::MQType(
kNumElementsInSyncQueue, true /* configure event flag word */,
std::move(ringbufferFd), kNumElementsInSyncQueue * kSyncElementSizeBytes);
ASSERT_NE(nullptr, mQueue);
ASSERT_TRUE(mQueue->isValid());
ASSERT_EQ(mQueue->getQuantumCount(), kNumElementsInSyncQueue);
// tell server to set up the queue on its end
ASSERT_TRUE(this->configureFmqSyncReadWrite(mQueue));
}
typename T::MQType* mQueue = nullptr;
};
TYPED_TEST_CASE(UnsynchronizedWriteClient, UnsyncTypes);
template <typename T>
class UnsynchronizedWriteClient : public ClientUnsyncTestBase<typename T::MQType> {
protected:
virtual void TearDown() { delete this->mQueue; }
virtual void SetUp() {
this->mService = this->waitGetTestService();
if (this->mService == nullptr) GTEST_SKIP() << "HIDL is not supported";
ASSERT_TRUE(this->mService->isRemote());
this->getFmqUnsyncWrite(true, false, this->mService, &this->mQueue);
ASSERT_NE(nullptr, this->mQueue);
ASSERT_TRUE(this->mQueue->isValid());
mNumMessagesMax = this->mQueue->getQuantumCount();
}
size_t mNumMessagesMax = 0;
};
/*
* Utility function to verify data read from the fast message queue.
*/
bool verifyData(int32_t* data, size_t count) {
for (size_t i = 0; i < count; i++) {
if (data[i] != i) return false;
}
return true;
}
/*
* Utility function to initialize data to be written to the FMQ
*/
inline void initData(int32_t* data, size_t count) {
for (size_t i = 0; i < count; i++) {
data[i] = i;
}
}
/*
* Verify that for an unsynchronized flavor of FMQ, multiple readers
* can recover from a write overflow condition.
*/
TYPED_TEST(UnsynchronizedWriteClientMultiProcess, MultipleReadersAfterOverflow) {
const size_t dataLen = 16;
pid_t pid;
/* creating first reader process */
if ((pid = fork()) == 0) {
typename TypeParam::MQType* queue = nullptr;
auto service =
this->getQueue(&queue, true /* setupQueue */, TypeParam::UserFd /* userFd */);
ASSERT_NE(service, nullptr);
ASSERT_TRUE(service->isRemote());
ASSERT_NE(queue, nullptr);
ASSERT_TRUE(queue->isValid());
size_t numMessagesMax = queue->getQuantumCount();
// The following two writes will cause a write overflow.
auto ret = this->requestWriteFmqUnsync(numMessagesMax, service);
ASSERT_TRUE(ret);
ret = this->requestWriteFmqUnsync(1, service);
ASSERT_TRUE(ret);
// The following read should fail due to the overflow.
std::vector<int32_t> readData(numMessagesMax);
ASSERT_FALSE(queue->read(&readData[0], numMessagesMax));
/*
* Request another write to verify that the reader can recover from the
* overflow condition.
*/
ASSERT_LT(dataLen, numMessagesMax);
ret = this->requestWriteFmqUnsync(dataLen, service);
ASSERT_TRUE(ret);
// Verify that the read is successful.
ASSERT_TRUE(queue->read(&readData[0], dataLen));
ASSERT_TRUE(verifyData(&readData[0], dataLen));
delete queue;
exit(0);
}
ASSERT_GT(pid, 0 /* parent should see PID greater than 0 for a good fork */);
int status;
// wait for the first reader process to exit.
ASSERT_EQ(pid, waitpid(pid, &status, 0 /* options */));
// creating second reader process.
if ((pid = fork()) == 0) {
typename TypeParam::MQType* queue = nullptr;
auto service = this->getQueue(&queue, false /* setupQueue */, false /* userFd */);
ASSERT_NE(service, nullptr);
ASSERT_TRUE(service->isRemote());
ASSERT_NE(queue, nullptr);
ASSERT_TRUE(queue->isValid());
// This read should fail due to the write overflow.
std::vector<int32_t> readData(dataLen);
ASSERT_FALSE(queue->read(&readData[0], dataLen));
/*
* Request another write to verify that the process that recover from
* the overflow condition.
*/
auto ret = this->requestWriteFmqUnsync(dataLen, service);
ASSERT_TRUE(ret);
// verify that the read is successful.
ASSERT_TRUE(queue->read(&readData[0], dataLen));
ASSERT_TRUE(verifyData(&readData[0], dataLen));
delete queue;
exit(0);
}
ASSERT_GT(pid, 0 /* parent should see PID greater than 0 for a good fork */);
ASSERT_EQ(pid, waitpid(pid, &status, 0 /* options */));
}
/*
* Test that basic blocking works using readBlocking()/writeBlocking() APIs
* using the EventFlag object owned by FMQ.
*/
TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWrite1) {
const size_t dataLen = 64;
bool ret = false;
/*
* Request service to perform a blocking read. This call is oneway and will
* return immediately.
*/
this->mService->requestBlockingRead(dataLen);
{
std::array<int32_t, dataLen> data = {0};
ret = this->mQueue->writeBlocking(
data.data(), data.size(),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
{
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
ret = this->mQueue->writeBlocking(
data.data(), data.size(),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
}
/*
* Test that basic blocking works using readBlocking()/writeBlocking() APIs
* using the EventFlag object owned by FMQ and using the default EventFlag
* notification bit mask.
*/
TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWrite2) {
const size_t dataLen = 64;
bool ret = false;
/*
* Request service to perform a blocking read using default EventFlag
* notification bit mask. This call is oneway and will
* return immediately.
*/
this->mService->requestBlockingReadDefaultEventFlagBits(dataLen);
/* Cause a context switch to allow service to block */
sched_yield();
{
std::array<int32_t, dataLen> data = {0};
ret = this->mQueue->writeBlocking(data.data(), data.size());
ASSERT_TRUE(ret);
}
/*
* If the blocking read was successful, another write of size
* kNumElementsInSyncQueue will succeed.
*/
{
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
ret = this->mQueue->writeBlocking(data.data(), data.size(), 5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
}
/*
* Test that repeated blocking reads and writes work using readBlocking()/writeBlocking() APIs
* using the EventFlag object owned by FMQ.
* Each write operation writes the same amount of data as a single read
* operation.
*/
TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWriteRepeat1) {
const size_t dataLen = 64;
bool ret = false;
/*
* Request service to perform a blocking read of 64 elements. This call is
* oneway and will return immediately.
*/
const size_t writeCount = kNumElementsInSyncQueue;
this->mService->requestBlockingReadRepeat(dataLen, writeCount);
/*
* Write 64 elements into the queue for the service to consume
*/
{
std::array<int32_t, dataLen> data = {0};
for (size_t i = 0; i < writeCount; i++) {
ret = this->mQueue->writeBlocking(
data.data(), data.size(),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
}
/*
* The queue should be totally empty now, so filling it up entirely with one
* blocking write should be successful.
*/
{
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
ret = this->mQueue->writeBlocking(
data.data(), data.size(),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
}
/*
* Test that repeated blocking reads and writes work using readBlocking()/writeBlocking() APIs
* using the EventFlag object owned by FMQ. Each read operation reads twice the
* amount of data as a single write.
*
*/
TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWriteRepeat2) {
const size_t dataLen = 64;
bool ret = false;
/*
* Request service to perform a repeated blocking read. This call is oneway
* and will return immediately. It will read 64 * 2 elements with each
* blocking read, for a total of writeCount / 2 calls.
*/
const size_t writeCount = kNumElementsInSyncQueue;
this->mService->requestBlockingReadRepeat(dataLen * 2, writeCount / 2);
/*
* Write 64 elements into the queue writeCount times
*/
{
std::array<int32_t, dataLen> data = {0};
for (size_t i = 0; i < writeCount; i++) {
ret = this->mQueue->writeBlocking(
data.data(), data.size(),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
}
/*
* The queue should be totally empty now, so filling it up entirely with one
* blocking write should be successful.
*/
{
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
ret = this->mQueue->writeBlocking(
data.data(), data.size(),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
}
/*
* Test that basic blocking works using readBlocking()/writeBlocking() APIs
* using the EventFlag object owned by FMQ. Each write operation writes twice
* the amount of data as a single read.
*/
TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWriteRepeat3) {
const size_t dataLen = 64;
bool ret = false;
/*
* Request service to perform a repeated blocking read. This call is oneway
* and will return immediately. It will read 64 / 2 elements with each
* blocking read, for a total of writeCount * 2 calls.
*/
size_t writeCount = 1024;
this->mService->requestBlockingReadRepeat(dataLen / 2, writeCount * 2);
/*
* Write 64 elements into the queue writeCount times
*/
{
std::array<int32_t, dataLen> data = {0};
for (size_t i = 0; i < writeCount; i++) {
ret = this->mQueue->writeBlocking(
data.data(), data.size(),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
}
/*
* The queue should be totally empty now, so filling it up entirely with one
* blocking write should be successful.
*/
{
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
ret = this->mQueue->writeBlocking(
data.data(), data.size(),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY),
5000000000 /* timeOutNanos */);
ASSERT_TRUE(ret);
}
}
/*
* Test that writeBlocking()/readBlocking() APIs do not block on
* attempts to write/read 0 messages and return true.
*/
TYPED_TEST(SynchronizedReadWriteClient, BlockingReadWriteZeroMessages) {
int32_t data = 0;
/*
* Trigger a blocking write for zero messages with no timeout.
*/
bool ret = this->mQueue->writeBlocking(
&data, 0, static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY));
ASSERT_TRUE(ret);
/*
* Trigger a blocking read for zero messages with no timeout.
*/
ret = this->mQueue->readBlocking(
&data, 0, static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_FULL),
static_cast<uint32_t>(ITestMsgQ::EventFlagBits::FMQ_NOT_EMPTY));
ASSERT_TRUE(ret);
}
/*
* Request mService to write a small number of messages
* to the FMQ. Read and verify data.
*/
TYPED_TEST(SynchronizedReadWriteClient, SmallInputReaderTest1) {
const size_t dataLen = 16;
ASSERT_LE(dataLen, kNumElementsInSyncQueue);
bool ret = this->requestWriteFmqSync(dataLen);
ASSERT_TRUE(ret);
int32_t readData[dataLen] = {};
ASSERT_TRUE(this->mQueue->read(readData, dataLen));
ASSERT_TRUE(verifyData(readData, dataLen));
}
/*
* Request mService to write a message to the queue followed by a beginRead().
* Get a pointer to the memory region for the that first message. Set the write
* counter to the last byte in the ring buffer. Request another write from
* mService. The write should fail because the write address is misaligned.
*/
TYPED_TEST(SynchronizedReadWriteClient, MisalignedWriteCounter) {
if (TypeParam::UserFd) {
// When using the second FD for the ring buffer, we can't get to the read/write
// counters from a pointer to the ring buffer, so no sense in testing.
GTEST_SKIP();
}
const size_t dataLen = 1;
ASSERT_LE(dataLen, kNumElementsInSyncQueue);
bool ret = this->requestWriteFmqSync(dataLen);
ASSERT_TRUE(ret);
// begin read and get a MemTransaction object for the first object in the queue
typename TypeParam::MQType::MemTransaction tx;
ASSERT_TRUE(this->mQueue->beginRead(dataLen, &tx));
// get a pointer to the beginning of the ring buffer
const auto& region = tx.getFirstRegion();
int32_t* firstStart = region.getAddress();
// because this is the first location in the ring buffer, we can get
// access to the read and write pointer stored in the fd. 8 bytes back for the
// write counter and 16 bytes back for the read counter
uint64_t* writeCntr = (uint64_t*)((uint8_t*)firstStart - 8);
// set it to point to the very last byte in the ring buffer
*(writeCntr) = this->mQueue->getQuantumCount() * this->mQueue->getQuantumSize() - 1;
ASSERT_TRUE(*writeCntr % sizeof(int32_t) != 0);
// this is not actually necessary, but it's the expected the pattern.
this->mQueue->commitRead(dataLen);
// This next write will be misaligned and will overlap outside of the ring buffer.
// The write should fail.
ret = this->requestWriteFmqSync(dataLen);
EXPECT_FALSE(ret);
}
/*
* Request mService to write a small number of messages
* to the FMQ. Read and verify each message using
* beginRead/Commit read APIs.
*/
TYPED_TEST(SynchronizedReadWriteClient, SmallInputReaderTest2) {
const size_t dataLen = 16;
ASSERT_LE(dataLen, kNumElementsInSyncQueue);
auto ret = this->requestWriteFmqSync(dataLen);
ASSERT_TRUE(ret);
typename TypeParam::MQType::MemTransaction tx;
ASSERT_TRUE(this->mQueue->beginRead(dataLen, &tx));
auto first = tx.getFirstRegion();
auto second = tx.getSecondRegion();
size_t firstRegionLength = first.getLength();
for (size_t i = 0; i < dataLen; i++) {
if (i < firstRegionLength) {
ASSERT_EQ(i, *(first.getAddress() + i));
} else {
ASSERT_EQ(i, *(second.getAddress() + i - firstRegionLength));
}
}
ASSERT_TRUE(this->mQueue->commitRead(dataLen));
}
/*
* Write a small number of messages to FMQ. Request
* mService to read and verify that the write was successful.
*/
TYPED_TEST(SynchronizedReadWriteClient, SmallInputWriterTest1) {
const size_t dataLen = 16;
ASSERT_LE(dataLen, kNumElementsInSyncQueue);
size_t originalCount = this->mQueue->availableToWrite();
int32_t data[dataLen];
initData(data, dataLen);
ASSERT_TRUE(this->mQueue->write(data, dataLen));
bool ret = this->requestReadFmqSync(dataLen);
ASSERT_TRUE(ret);
size_t availableCount = this->mQueue->availableToWrite();
ASSERT_EQ(originalCount, availableCount);
}
/*
* Write a small number of messages to FMQ using the beginWrite()/CommitWrite()
* APIs. Request mService to read and verify that the write was successful.
*/
TYPED_TEST(SynchronizedReadWriteClient, SmallInputWriterTest2) {
const size_t dataLen = 16;
ASSERT_LE(dataLen, kNumElementsInSyncQueue);
size_t originalCount = this->mQueue->availableToWrite();
int32_t data[dataLen];
initData(data, dataLen);
typename TypeParam::MQType::MemTransaction tx;
ASSERT_TRUE(this->mQueue->beginWrite(dataLen, &tx));
auto first = tx.getFirstRegion();
auto second = tx.getSecondRegion();
size_t firstRegionLength = first.getLength();
int32_t* firstBaseAddress = first.getAddress();
int32_t* secondBaseAddress = second.getAddress();
for (size_t i = 0; i < dataLen; i++) {
if (i < firstRegionLength) {
*(firstBaseAddress + i) = i;
} else {
*(secondBaseAddress + i - firstRegionLength) = i;
}
}
ASSERT_TRUE(this->mQueue->commitWrite(dataLen));
auto ret = this->requestReadFmqSync(dataLen);
// ASSERT_TRUE(ret.isOk());
ASSERT_TRUE(ret);
size_t availableCount = this->mQueue->availableToWrite();
ASSERT_EQ(originalCount, availableCount);
}
/*
* Verify that the FMQ is empty and read fails when it is empty.
*/
TYPED_TEST(SynchronizedReadWriteClient, ReadWhenEmpty) {
ASSERT_EQ(0UL, this->mQueue->availableToRead());
const size_t numMessages = 2;
ASSERT_LE(numMessages, kNumElementsInSyncQueue);
int32_t readData[numMessages];
ASSERT_FALSE(this->mQueue->read(readData, numMessages));
}
/*
* Verify FMQ is empty.
* Write enough messages to fill it.
* Verify availableToWrite() method returns is zero.
* Try writing another message and verify that
* the attempted write was unsuccessful. Request mService
* to read and verify the messages in the FMQ.
*/
TYPED_TEST(SynchronizedReadWriteClient, WriteWhenFull) {
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
initData(data.data(), data.size());
ASSERT_TRUE(this->mQueue->write(data.data(), data.size()));
ASSERT_EQ(0UL, this->mQueue->availableToWrite());
ASSERT_FALSE(this->mQueue->write(&data[0], 1));
bool ret = this->requestReadFmqSync(data.size());
ASSERT_TRUE(ret);
}
/*
* Verify FMQ is empty.
* Request mService to write data equal to queue size.
* Read and verify data in mQueue.
*/
TYPED_TEST(SynchronizedReadWriteClient, LargeInputTest1) {
bool ret = this->requestWriteFmqSync(kNumElementsInSyncQueue);
ASSERT_TRUE(ret);
std::vector<int32_t> readData(kNumElementsInSyncQueue);
ASSERT_TRUE(this->mQueue->read(&readData[0], kNumElementsInSyncQueue));
ASSERT_TRUE(verifyData(&readData[0], kNumElementsInSyncQueue));
}
/*
* Request mService to write more than maximum number of messages to the FMQ.
* Verify that the write fails. Verify that availableToRead() method
* still returns 0 and verify that attempt to read fails.
*/
TYPED_TEST(SynchronizedReadWriteClient, LargeInputTest2) {
ASSERT_EQ(0UL, this->mQueue->availableToRead());
const size_t numMessages = 2048;
ASSERT_GT(numMessages, kNumElementsInSyncQueue);
bool ret = this->requestWriteFmqSync(numMessages);
ASSERT_FALSE(ret);
int32_t readData;
ASSERT_EQ(0UL, this->mQueue->availableToRead());
ASSERT_FALSE(this->mQueue->read(&readData, 1));
}
/*
* Write until FMQ is full.
* Verify that the number of messages available to write
* is equal to mNumMessagesMax.
* Verify that another write attempt fails.
* Request mService to read. Verify read count.
*/
TYPED_TEST(SynchronizedReadWriteClient, LargeInputTest3) {
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
initData(data.data(), data.size());
ASSERT_TRUE(this->mQueue->write(data.data(), data.size()));
ASSERT_EQ(0UL, this->mQueue->availableToWrite());
ASSERT_FALSE(this->mQueue->write(data.data(), 1));
bool ret = this->requestReadFmqSync(data.size());
ASSERT_TRUE(ret);
}
/*
* Confirm that the FMQ is empty. Request mService to write to FMQ.
* Do multiple reads to empty FMQ and verify data.
*/
TYPED_TEST(SynchronizedReadWriteClient, MultipleRead) {
const size_t chunkSize = 100;
const size_t chunkNum = 5;
const size_t numMessages = chunkSize * chunkNum;
ASSERT_LE(numMessages, kNumElementsInSyncQueue);
size_t availableToRead = this->mQueue->availableToRead();
size_t expectedCount = 0;
ASSERT_EQ(expectedCount, availableToRead);
bool ret = this->requestWriteFmqSync(numMessages);
ASSERT_TRUE(ret);
int32_t readData[numMessages] = {};
for (size_t i = 0; i < chunkNum; i++) {
ASSERT_TRUE(this->mQueue->read(readData + i * chunkSize, chunkSize));
}
ASSERT_TRUE(verifyData(readData, numMessages));
}
/*
* Write to FMQ in bursts.
* Request mService to read data. Verify the read was successful.
*/
TYPED_TEST(SynchronizedReadWriteClient, MultipleWrite) {
const size_t chunkSize = 100;
const size_t chunkNum = 5;
const size_t numMessages = chunkSize * chunkNum;
ASSERT_LE(numMessages, kNumElementsInSyncQueue);
int32_t data[numMessages];
initData(&data[0], numMessages);
for (size_t i = 0; i < chunkNum; i++) {
ASSERT_TRUE(this->mQueue->write(data + i * chunkSize, chunkSize));
}
bool ret = this->requestReadFmqSync(numMessages);
ASSERT_TRUE(ret);
}
/*
* Write enough messages into the FMQ to fill half of it.
* Request mService to read back the same.
* Write mNumMessagesMax messages into the queue. This should cause a
* wrap around. Request mService to read and verify the data.
*/
TYPED_TEST(SynchronizedReadWriteClient, ReadWriteWrapAround) {
size_t numMessages = kNumElementsInSyncQueue / 2;
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
initData(data.data(), data.size());
ASSERT_TRUE(this->mQueue->write(&data[0], numMessages));
bool ret = this->requestReadFmqSync(numMessages);
ASSERT_TRUE(ret);
ASSERT_TRUE(this->mQueue->write(data.data(), data.size()));
ret = this->requestReadFmqSync(data.size());
ASSERT_TRUE(ret);
}
/*
* Use beginWrite/commitWrite/getSlot APIs to test wrap arounds are handled
* correctly.
* Write enough messages into the FMQ to fill half of it
* and read back the same.
* Write mNumMessagesMax messages into the queue. This will cause a
* wrap around. Read and verify the data.
*/
TYPED_TEST(SynchronizedReadWriteClient, ReadWriteWrapAround2) {
size_t numMessages = kNumElementsInSyncQueue / 2;
std::vector<int32_t> data(kNumElementsInSyncQueue, 0);
initData(data.data(), data.size());
ASSERT_TRUE(this->mQueue->write(&data[0], numMessages));
auto ret = this->requestReadFmqSync(numMessages);
ASSERT_TRUE(ret);
/*
* The next write and read will have to deal with with wrap arounds.
*/
typename TypeParam::MQType::MemTransaction tx;
ASSERT_TRUE(this->mQueue->beginWrite(data.size(), &tx));
ASSERT_EQ(tx.getFirstRegion().getLength() + tx.getSecondRegion().getLength(), data.size());
for (size_t i = 0; i < data.size(); i++) {
int32_t* ptr = tx.getSlot(i);
*ptr = data[i];
}
ASSERT_TRUE(this->mQueue->commitWrite(data.size()));
ret = this->requestReadFmqSync(data.size());
ASSERT_TRUE(ret);
}
/*
* Request this->mService to write a small number of messages
* to the FMQ. Read and verify data.
*/
TYPED_TEST(UnsynchronizedWriteClient, SmallInputReaderTest1) {
const size_t dataLen = 16;
ASSERT_LE(dataLen, this->mNumMessagesMax);
bool ret = this->requestWriteFmqUnsync(dataLen, this->mService);
ASSERT_TRUE(ret);
int32_t readData[dataLen] = {};
ASSERT_TRUE(this->mQueue->read(readData, dataLen));
ASSERT_TRUE(verifyData(readData, dataLen));
}
/*
* Write a small number of messages to FMQ. Request
* this->mService to read and verify that the write was successful.
*/
TYPED_TEST(UnsynchronizedWriteClient, SmallInputWriterTest1) {
const size_t dataLen = 16;
ASSERT_LE(dataLen, this->mNumMessagesMax);
ASSERT_TRUE(this->requestWriteFmqUnsync(dataLen, this->mService));
ASSERT_TRUE(this->requestReadFmqUnsync(dataLen, this->mService));
}
/*
* Verify that the FMQ is empty and read fails when it is empty.
*/
TYPED_TEST(UnsynchronizedWriteClient, ReadWhenEmpty) {
ASSERT_EQ(0UL, this->mQueue->availableToRead());
const size_t numMessages = 2;
ASSERT_LE(numMessages, this->mNumMessagesMax);
int32_t readData[numMessages];
ASSERT_FALSE(this->mQueue->read(readData, numMessages));
}
/*
* Verify FMQ is empty.
* Write enough messages to fill it.
* Verify availableToWrite() method returns is zero.
* Try writing another message and verify that
* the attempted write was successful. Request this->mService
* to read the messages in the FMQ and verify that it is unsuccessful.
*/
TYPED_TEST(UnsynchronizedWriteClient, WriteWhenFull) {
std::vector<int32_t> data(this->mNumMessagesMax);
initData(&data[0], this->mNumMessagesMax);
ASSERT_TRUE(this->requestWriteFmqUnsync(this->mNumMessagesMax, this->mService));
ASSERT_EQ(0UL, this->mQueue->availableToWrite());
ASSERT_TRUE(this->requestWriteFmqUnsync(1, this->mService));
bool ret = this->requestReadFmqUnsync(this->mNumMessagesMax, this->mService);
ASSERT_FALSE(ret);
}
/*
* Verify FMQ is empty.
* Request this->mService to write data equal to queue size.
* Read and verify data in this->mQueue.
*/
TYPED_TEST(UnsynchronizedWriteClient, LargeInputTest1) {
bool ret = this->requestWriteFmqUnsync(this->mNumMessagesMax, this->mService);
ASSERT_TRUE(ret);
std::vector<int32_t> data(this->mNumMessagesMax);
ASSERT_TRUE(this->mQueue->read(&data[0], this->mNumMessagesMax));
ASSERT_TRUE(verifyData(&data[0], this->mNumMessagesMax));
}
/*
* Request this->mService to write more than maximum number of messages to the FMQ.
* Verify that the write fails. Verify that availableToRead() method
* still returns 0 and verify that attempt to read fails.
*/
TYPED_TEST(UnsynchronizedWriteClient, LargeInputTest2) {
ASSERT_EQ(0UL, this->mQueue->availableToRead());
const size_t numMessages = this->mNumMessagesMax + 1;
bool ret = this->requestWriteFmqUnsync(numMessages, this->mService);
ASSERT_FALSE(ret);
int32_t readData;
ASSERT_EQ(0UL, this->mQueue->availableToRead());
ASSERT_FALSE(this->mQueue->read(&readData, 1));
}
/*
* Write until FMQ is full.
* Verify that another write attempt is successful.
* Request this->mService to read. Verify that read is unsuccessful
* because of the write rollover.
* Perform another write and verify that the read is successful
* to check if the reader process can recover from the error condition.
*/
TYPED_TEST(UnsynchronizedWriteClient, LargeInputTest3) {
ASSERT_TRUE(this->requestWriteFmqUnsync(this->mNumMessagesMax, this->mService));
ASSERT_EQ(0UL, this->mQueue->availableToWrite());
ASSERT_TRUE(this->requestWriteFmqUnsync(1, this->mService));
bool ret = this->requestReadFmqUnsync(this->mNumMessagesMax, this->mService);
ASSERT_FALSE(ret);
ASSERT_TRUE(this->requestWriteFmqUnsync(this->mNumMessagesMax, this->mService));
ret = this->requestReadFmqUnsync(this->mNumMessagesMax, this->mService);
ASSERT_TRUE(ret);
}
/*
* Confirm that the FMQ is empty. Request this->mService to write to FMQ.
* Do multiple reads to empty FMQ and verify data.
*/
TYPED_TEST(UnsynchronizedWriteClient, MultipleRead) {
const size_t chunkSize = 100;
const size_t chunkNum = 5;
const size_t numMessages = chunkSize * chunkNum;
ASSERT_LE(numMessages, this->mNumMessagesMax);
size_t availableToRead = this->mQueue->availableToRead();
size_t expectedCount = 0;
ASSERT_EQ(expectedCount, availableToRead);
bool ret = this->requestWriteFmqUnsync(numMessages, this->mService);
ASSERT_TRUE(ret);
int32_t readData[numMessages] = {};
for (size_t i = 0; i < chunkNum; i++) {
ASSERT_TRUE(this->mQueue->read(readData + i * chunkSize, chunkSize));
}
ASSERT_TRUE(verifyData(readData, numMessages));
}
/*
* Write to FMQ in bursts.
* Request this->mService to read data, verify that it was successful.
*/
TYPED_TEST(UnsynchronizedWriteClient, MultipleWrite) {
const size_t chunkSize = 100;
const size_t chunkNum = 5;
const size_t numMessages = chunkSize * chunkNum;
ASSERT_LE(numMessages, this->mNumMessagesMax);
for (size_t i = 0; i < chunkNum; i++) {
ASSERT_TRUE(this->requestWriteFmqUnsync(chunkSize, this->mService));
}
ASSERT_EQ(numMessages, this->mQueue->availableToRead());
int32_t readData[numMessages] = {};
ASSERT_TRUE(this->mQueue->read(readData, numMessages));
// verify that data is filled by the service - the messages will contiain
// 'chunkSize' because that's the value we passed to the service each write.
ASSERT_TRUE(verifyData(readData, chunkSize));
}
/*
* Write enough messages into the FMQ to fill half of it.
* Request this->mService to read back the same.
* Write this->mNumMessagesMax messages into the queue. This should cause a
* wrap around. Request this->mService to read and verify the data.
*/
TYPED_TEST(UnsynchronizedWriteClient, ReadWriteWrapAround) {
size_t numMessages = this->mNumMessagesMax / 2;
ASSERT_TRUE(this->requestWriteFmqUnsync(numMessages, this->mService));
ASSERT_TRUE(this->requestReadFmqUnsync(numMessages, this->mService));
ASSERT_TRUE(this->requestWriteFmqUnsync(this->mNumMessagesMax, this->mService));
ASSERT_TRUE(this->requestReadFmqUnsync(this->mNumMessagesMax, this->mService));
}
/*
* Request this->mService to write a small number of messages
* to the FMQ. Read and verify data from two threads configured
* as readers to the FMQ.
*/
TYPED_TEST(UnsynchronizedWriteClient, SmallInputMultipleReaderTest) {
typename TypeParam::MQType* mQueue2 = this->newQueue();
ASSERT_NE(nullptr, mQueue2);
const size_t dataLen = 16;
ASSERT_LE(dataLen, this->mNumMessagesMax);
bool ret = this->requestWriteFmqUnsync(dataLen, this->mService);
ASSERT_TRUE(ret);
pid_t pid;
if ((pid = fork()) == 0) {
/* child process */
int32_t readData[dataLen] = {};
ASSERT_TRUE(mQueue2->read(readData, dataLen));
ASSERT_TRUE(verifyData(readData, dataLen));
exit(0);
} else {
ASSERT_GT(pid,
0 /* parent should see PID greater than 0 for a good fork */);
int32_t readData[dataLen] = {};
ASSERT_TRUE(this->mQueue->read(readData, dataLen));
ASSERT_TRUE(verifyData(readData, dataLen));
}
}
/*
* Request this->mService to write into the FMQ until it is full.
* Request this->mService to do another write and verify it is successful.
* Use two reader processes to read and verify that both fail.
*/
TYPED_TEST(UnsynchronizedWriteClient, OverflowNotificationTest) {
typename TypeParam::MQType* mQueue2 = this->newQueue();
ASSERT_NE(nullptr, mQueue2);
bool ret = this->requestWriteFmqUnsync(this->mNumMessagesMax, this->mService);
ASSERT_TRUE(ret);
ret = this->requestWriteFmqUnsync(1, this->mService);
ASSERT_TRUE(ret);
pid_t pid;
if ((pid = fork()) == 0) {
/* child process */
std::vector<int32_t> readData(this->mNumMessagesMax);
ASSERT_FALSE(mQueue2->read(&readData[0], this->mNumMessagesMax));
exit(0);
} else {
ASSERT_GT(pid, 0/* parent should see PID greater than 0 for a good fork */);
std::vector<int32_t> readData(this->mNumMessagesMax);
ASSERT_FALSE(this->mQueue->read(&readData[0], this->mNumMessagesMax));
}
}
/*
* Make sure a valid queue can be created with different supported types.
* All fundamental or native types should work. An AIDL parcelable that is
* annotated with @FixedSize is supported. A parcelable without it, will cause
* a compilation error.
*/
typedef ::testing::Types<FixedParcelable, FixedUnion, EventFlagBits, bool, int8_t, char, char16_t,
int32_t, int64_t, float, double>
AidlTypeCheckTypes;
template <typename T>
class AidlTypeChecks : public ::testing::Test {};
TYPED_TEST_CASE(AidlTypeChecks, AidlTypeCheckTypes);
TYPED_TEST(AidlTypeChecks, FixedSizeParcelableTest) {
android::AidlMessageQueue<TypeParam, UnsynchronizedWrite> queue =
android::AidlMessageQueue<TypeParam, UnsynchronizedWrite>(64);
ASSERT_TRUE(queue.isValid());
// Make sure we can do a simple write/read of any value.
TypeParam writeData[1];
TypeParam readData[1];
EXPECT_TRUE(queue.write(writeData, 1));
EXPECT_TRUE(queue.read(readData, 1));
}