blob: 599889caf2b0ed03c6be5aa96f5f806500568802 [file] [log] [blame]
/*
* 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 <BnBinderRecordReplayTest.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/RecordedTransaction.h>
#include <gtest/gtest.h>
#include <sys/prctl.h>
using namespace android;
using android::binder::Status;
using android::binder::debug::RecordedTransaction;
const String16 kServerName = String16("binderRecordReplay");
#define GENERATE_GETTER_SETTER(name, T) \
Status set##name(T input) { \
m##name = input; \
return Status::ok(); \
} \
\
Status get##name(T* output) { \
*output = m##name; \
return Status::ok(); \
} \
T m##name
class MyRecordReplay : public BnBinderRecordReplayTest {
public:
GENERATE_GETTER_SETTER(Boolean, bool);
GENERATE_GETTER_SETTER(Byte, int8_t);
GENERATE_GETTER_SETTER(Int, int);
GENERATE_GETTER_SETTER(Char, char16_t);
GENERATE_GETTER_SETTER(Long, int64_t);
GENERATE_GETTER_SETTER(Float, float);
GENERATE_GETTER_SETTER(Double, double);
};
class BinderClearBuf : public ::testing::Test {
public:
void SetUp() override {
// get the remote service
mBinder = defaultServiceManager()->getService(kServerName);
ASSERT_NE(nullptr, mBinder);
mInterface = interface_cast<IBinderRecordReplayTest>(mBinder);
mBpBinder = mBinder->remoteBinder();
ASSERT_NE(nullptr, mBpBinder);
}
template <typename T>
void recordReplay(Status (IBinderRecordReplayTest::*set)(T), T recordedValue,
Status (IBinderRecordReplayTest::*get)(T*), T changedValue) {
base::unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec",
O_RDWR | O_CREAT | O_CLOEXEC, 0666));
ASSERT_TRUE(fd.ok());
// record a transaction
mBpBinder->startRecordingBinder(fd);
auto status = (*mInterface.*set)(recordedValue);
EXPECT_TRUE(status.isOk());
mBpBinder->stopRecordingBinder();
// test transaction does the thing we expect it to do
T output;
status = (*mInterface.*get)(&output);
EXPECT_TRUE(status.isOk());
EXPECT_EQ(output, recordedValue);
// write over the existing state
status = (*mInterface.*set)(changedValue);
EXPECT_TRUE(status.isOk());
status = (*mInterface.*get)(&output);
EXPECT_TRUE(status.isOk());
EXPECT_EQ(output, changedValue);
// replay transaction
ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET));
std::optional<RecordedTransaction> transaction = RecordedTransaction::fromFile(fd);
ASSERT_NE(transaction, std::nullopt);
// TODO: move logic to replay RecordedTransaction into RecordedTransaction
Parcel data;
data.setData(transaction->getDataParcel().data(), transaction->getDataParcel().dataSize());
auto result = mBinder->remoteBinder()->transact(transaction->getCode(), data, nullptr,
transaction->getFlags());
// make sure recording does the thing we expect it to do
EXPECT_EQ(OK, result);
status = (*mInterface.*get)(&output);
EXPECT_TRUE(status.isOk());
EXPECT_EQ(output, recordedValue);
}
private:
sp<IBinder> mBinder;
sp<BpBinder> mBpBinder;
sp<IBinderRecordReplayTest> mInterface;
};
TEST_F(BinderClearBuf, RecordReplayRepeatByte) {
recordReplay(&IBinderRecordReplayTest::setByte, int8_t{122}, &IBinderRecordReplayTest::getByte,
int8_t{90});
}
TEST_F(BinderClearBuf, RecordReplayRepeatBoolean) {
recordReplay(&IBinderRecordReplayTest::setBoolean, true, &IBinderRecordReplayTest::getBoolean,
false);
}
TEST_F(BinderClearBuf, RecordReplayRepeatChar) {
recordReplay(&IBinderRecordReplayTest::setChar, char16_t{'G'},
&IBinderRecordReplayTest::getChar, char16_t{'K'});
}
TEST_F(BinderClearBuf, RecordReplayRepeatInt) {
recordReplay(&IBinderRecordReplayTest::setInt, 3, &IBinderRecordReplayTest::getInt, 5);
}
TEST_F(BinderClearBuf, RecordReplayRepeatFloat) {
recordReplay(&IBinderRecordReplayTest::setFloat, 1.1f, &IBinderRecordReplayTest::getFloat,
22.0f);
}
TEST_F(BinderClearBuf, RecordReplayRepeatLong) {
recordReplay(&IBinderRecordReplayTest::setLong, int64_t{1LL << 55},
&IBinderRecordReplayTest::getLong, int64_t{1LL << 12});
}
TEST_F(BinderClearBuf, RecordReplayRepeatDouble) {
recordReplay(&IBinderRecordReplayTest::setDouble, 0.00, &IBinderRecordReplayTest::getDouble,
1.11);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
auto server = sp<MyRecordReplay>::make();
android::defaultServiceManager()->addService(kServerName, server.get());
IPCThreadState::self()->joinThreadPool(true);
exit(1); // should not reach
}
// not racey, but getService sleeps for 1s
usleep(100000);
return RUN_ALL_TESTS();
}