// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/android/binder.h"
#include <android/binder_status.h>
#include <algorithm>
#include <atomic>
#include <limits>
#include <utility>
#include "base/command_line.h"
#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ref.h"
#include "base/notreached.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/bind.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_timeouts.h"
#include "base/types/expected_macros.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace base::android {
namespace {
class BinderTest : public testing::Test {
public:
void SetUp() override {
if (!IsNativeBinderAvailable()) {
GTEST_SKIP() << "This test is only meaningful when run on Android Q+ "
<< "with the binder NDK library available.";
}
}
};
DEFINE_BINDER_CLASS(ReflectorClass);
class Reflector : public SupportsBinder<ReflectorClass> {
public:
using ReflectCallback = OnceCallback<void(const ParcelReader&)>;
template <typename WriteFn, typename ReadFn>
void Reflect(WriteFn writer, ReadFn reader) {
auto binder = GetBinder();
auto parcel = *binder.PrepareTransaction();
writer(parcel.writer());
reflected_.Reset();
reflect_ = BindLambdaForTesting(reader);
EXPECT_TRUE(binder.TransactOneWay(1, std::move(parcel)).has_value());
reflected_.Wait();
}
private:
~Reflector() override = default;
// SupportsBinder<ReflectorClass>:
BinderStatusOr<void> OnBinderTransaction(transaction_code_t code,
const ParcelReader& reader,
const ParcelWriter& out) override {
std::move(reflect_).Run(reader);
reflected_.Signal();
return ok();
}
ReflectCallback reflect_;
WaitableEvent reflected_;
};
TEST_F(BinderTest, ReadWriteInt32) {
auto reflector = MakeRefCounted<Reflector>();
reflector->Reflect(
[](const ParcelWriter& writer) {
EXPECT_TRUE(writer.WriteInt32(42).has_value());
EXPECT_TRUE(writer.WriteInt32(-42).has_value());
EXPECT_TRUE(
writer.WriteInt32(std::numeric_limits<int32_t>::min()).has_value());
EXPECT_TRUE(
writer.WriteInt32(std::numeric_limits<int32_t>::max()).has_value());
},
[](const ParcelReader& reader) {
EXPECT_EQ(42, *reader.ReadInt32());
EXPECT_EQ(-42, *reader.ReadInt32());
EXPECT_EQ(std::numeric_limits<int32_t>::min(), *reader.ReadInt32());
EXPECT_EQ(std::numeric_limits<int32_t>::max(), *reader.ReadInt32());
});
}
TEST_F(BinderTest, ReadWriteUint32) {
auto reflector = MakeRefCounted<Reflector>();
reflector->Reflect(
[](const ParcelWriter& writer) {
EXPECT_TRUE(writer.WriteUint32(42).has_value());
EXPECT_TRUE(writer.WriteUint32(std::numeric_limits<uint32_t>::min())
.has_value());
EXPECT_TRUE(writer.WriteUint32(std::numeric_limits<uint32_t>::max())
.has_value());
},
[](const ParcelReader& reader) {
EXPECT_EQ(42u, *reader.ReadUint32());
EXPECT_EQ(std::numeric_limits<uint32_t>::min(), *reader.ReadUint32());
EXPECT_EQ(std::numeric_limits<uint32_t>::max(), *reader.ReadUint32());
});
}
TEST_F(BinderTest, ReadWriteUint64) {
auto reflector = MakeRefCounted<Reflector>();
reflector->Reflect(
[](const ParcelWriter& writer) {
EXPECT_TRUE(writer.WriteUint64(42).has_value());
EXPECT_TRUE(writer.WriteUint64(std::numeric_limits<uint64_t>::min())
.has_value());
EXPECT_TRUE(writer.WriteUint64(std::numeric_limits<uint64_t>::max())
.has_value());
},
[](const ParcelReader& reader) {
EXPECT_EQ(42u, *reader.ReadUint64());
EXPECT_EQ(std::numeric_limits<uint64_t>::min(), *reader.ReadUint64());
EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *reader.ReadUint64());
});
}
TEST_F(BinderTest, ReadWriteByteArray) {
auto reflector = MakeRefCounted<Reflector>();
const uint8_t kPrimeData[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
reflector->Reflect(
[&](const ParcelWriter& writer) {
EXPECT_TRUE(writer.WriteByteArray(kPrimeData).has_value());
},
[&](const ParcelReader& reader) {
std::vector<uint8_t> buffer;
EXPECT_TRUE(reader
.ReadByteArray([&](size_t size) {
buffer.resize(size);
return buffer.data();
})
.has_value());
EXPECT_TRUE(std::equal(buffer.begin(), buffer.end(),
std::begin(kPrimeData), std::end(kPrimeData)));
});
}
TEST_F(BinderTest, ReadWriteEmptyByteArray) {
auto reflector = MakeRefCounted<Reflector>();
reflector->Reflect(
[&](const ParcelWriter& writer) {
EXPECT_TRUE(writer.WriteByteArray({}).has_value());
},
[](const ParcelReader& reader) {
EXPECT_TRUE(reader
.ReadByteArray([](size_t size) -> uint8_t* {
// We don't call the allocator for empty arrays.
NOTREACHED();
})
.has_value());
});
}
TEST_F(BinderTest, ReadWriteFileDescriptor) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
File file(temp_dir.GetPath().AppendASCII("test_file"),
File::Flags::FLAG_CREATE | File::Flags::FLAG_WRITE);
auto reflector = MakeRefCounted<Reflector>();
reflector->Reflect(
[&](const ParcelWriter& writer) {
EXPECT_TRUE(
writer.WriteFileDescriptor(ScopedFD(file.TakePlatformFile()))
.has_value());
},
[](const ParcelReader& reader) {
ScopedFD fd = *reader.ReadFileDescriptor();
EXPECT_TRUE(fd.is_valid());
});
}
class AddInterface {
public:
DEFINE_BINDER_CLASS(Class);
static constexpr transaction_code_t kAdd = 1234;
class Proxy : public Class::BinderRef {
public:
explicit Proxy(BinderRef binder) : Class::BinderRef(std::move(binder)) {}
int32_t Add(int32_t n) {
const Parcel sum =
*Transact(kAdd, [n](const auto& p) { return p.WriteInt32(n); });
return *sum.reader().ReadInt32();
}
};
};
// Test service which adds a fixed offset to any transacted values;
class AddService : public SupportsBinder<AddInterface::Class> {
public:
explicit AddService(int32_t offset) : offset_(offset) {}
void set_destruction_callback(base::OnceClosure callback) {
destruction_callback_ = std::move(callback);
}
void set_binder_destruction_callback(base::RepeatingClosure callback) {
binder_destruction_callback_ = std::move(callback);
}
// SupportsBinder<AddInterface::Class>:
BinderStatusOr<void> OnBinderTransaction(transaction_code_t code,
const ParcelReader& in,
const ParcelWriter& out) override {
EXPECT_EQ(AddInterface::kAdd, code);
return out.WriteInt32(*in.ReadInt32() + offset_);
}
void OnBinderDestroyed() override {
if (binder_destruction_callback_) {
binder_destruction_callback_.Run();
}
}
private:
~AddService() override {
if (destruction_callback_) {
std::move(destruction_callback_).Run();
}
}
const int32_t offset_;
base::OnceClosure destruction_callback_;
base::RepeatingClosure binder_destruction_callback_;
};
TEST_F(BinderTest, NullBinderRef) {
BinderRef ref;
EXPECT_FALSE(ref);
TypedBinderRef<AddInterface::Class> typed_ref(std::move(ref));
EXPECT_FALSE(typed_ref);
}
TEST_F(BinderTest, BasicTransaction) {
auto add42_service = base::MakeRefCounted<AddService>(42);
AddInterface::Proxy add42(add42_service->GetBinder());
EXPECT_EQ(47, add42.Add(5));
}
TEST_F(BinderTest, Lifecycle) {
auto add42_service = base::MakeRefCounted<AddService>(42);
AddInterface::Proxy add42(add42_service->GetBinder());
bool is_destroyed = false;
base::WaitableEvent destruction;
add42_service->set_destruction_callback(base::BindLambdaForTesting([&] {
is_destroyed = true;
destruction.Signal();
}));
add42_service.reset();
EXPECT_FALSE(is_destroyed);
EXPECT_EQ(47, add42.Add(5));
add42.reset();
destruction.Wait();
EXPECT_TRUE(is_destroyed);
}
TEST_F(BinderTest, OnBinderDestroyed) {
auto add5_service = base::MakeRefCounted<AddService>(5);
bool has_binder = true;
base::WaitableEvent binder_destruction;
add5_service->set_binder_destruction_callback(base::BindLambdaForTesting([&] {
has_binder = false;
binder_destruction.Signal();
}));
AddInterface::Proxy add5(add5_service->GetBinder());
EXPECT_TRUE(has_binder);
EXPECT_EQ(12, add5.Add(7));
add5.reset();
binder_destruction.Wait();
EXPECT_FALSE(has_binder);
binder_destruction.Reset();
has_binder = true;
AddInterface::Proxy add5_1(add5_service->GetBinder());
AddInterface::Proxy add5_2(add5_service->GetBinder());
EXPECT_EQ(6, add5_1.Add(1));
EXPECT_EQ(7, add5_2.Add(2));
add5_1.reset();
EXPECT_TRUE(has_binder);
add5_2.reset();
binder_destruction.Wait();
EXPECT_FALSE(has_binder);
}
class MultiplyInterface {
public:
DEFINE_BINDER_CLASS(Class);
static constexpr transaction_code_t kMultiply = 5678;
class Proxy : public Class::BinderRef {
public:
explicit Proxy(BinderRef binder) : Class::BinderRef(std::move(binder)) {}
int32_t Multiply(int32_t n) {
const Parcel product =
*Transact(kMultiply, [n](const auto& p) { return p.WriteInt32(n); });
return *product.reader().ReadInt32();
}
};
};
// Test service which multiplies transacted values by a fixed scale.
class MultiplyService : public SupportsBinder<MultiplyInterface::Class> {
public:
explicit MultiplyService(int32_t scale) : scale_(scale) {}
void set_destruction_callback(base::OnceClosure callback) {
destruction_callback_ = std::move(callback);
}
// SupportsBinder<MultiplyInterface::Class>:
BinderStatusOr<void> OnBinderTransaction(transaction_code_t code,
const ParcelReader& in,
const ParcelWriter& out) override {
EXPECT_EQ(MultiplyInterface::kMultiply, code);
return out.WriteInt32(*in.ReadInt32() * scale_);
}
private:
~MultiplyService() override {
if (destruction_callback_) {
std::move(destruction_callback_).Run();
}
}
const int32_t scale_;
base::OnceClosure destruction_callback_;
};
TEST_F(BinderTest, MultipleInstances) {
auto add100_service = base::MakeRefCounted<AddService>(100);
auto add200_service = base::MakeRefCounted<AddService>(200);
AddInterface::Proxy add100(add100_service->GetBinder());
AddInterface::Proxy add200(add200_service->GetBinder());
EXPECT_EQ(105, add100.Add(5));
EXPECT_EQ(207, add200.Add(7));
}
TEST_F(BinderTest, MultipleClasses) {
auto add100_service = base::MakeRefCounted<AddService>(100);
auto multiply7_service = base::MakeRefCounted<MultiplyService>(7);
AddInterface::Proxy add100(add100_service->GetBinder());
MultiplyInterface::Proxy multiply7(multiply7_service->GetBinder());
EXPECT_EQ(105, add100.Add(5));
EXPECT_EQ(63, multiply7.Multiply(9));
}
class MathInterface {
public:
DEFINE_BINDER_CLASS(Class);
static constexpr transaction_code_t kGetAdd = 1;
static constexpr transaction_code_t kGetMultiply = 2;
class Proxy : public Class::BinderRef {
public:
explicit Proxy(BinderRef binder) : Class::BinderRef(std::move(binder)) {}
AddInterface::Proxy GetAdd(int32_t offset) {
auto reply = *Transact(
kGetAdd, [offset](const auto& p) { return p.WriteInt32(offset); });
return AddInterface::Proxy(*reply.reader().ReadBinder());
}
MultiplyInterface::Proxy GetMultiply(int32_t scale) {
auto reply = *Transact(
kGetMultiply, [scale](const auto& p) { return p.WriteInt32(scale); });
return MultiplyInterface::Proxy(*reply.reader().ReadBinder());
}
};
};
// A service which expects transactions requesting new AddInterface or
// MultiplyInterface binders with a respective offset or scale. Each request
// returns a binder for a bespoke service instance configured accordingly.
class MathService : public SupportsBinder<MathInterface::Class> {
public:
// SupportsBinder<MathInterface::Class>:
BinderStatusOr<void> OnBinderTransaction(transaction_code_t code,
const ParcelReader& in,
const ParcelWriter& out) override {
ASSIGN_OR_RETURN(const int32_t value, in.ReadInt32());
switch (code) {
case MathInterface::kGetAdd: {
auto service = base::MakeRefCounted<AddService>(value);
RETURN_IF_ERROR(out.WriteBinder(service->GetBinder()));
service->set_destruction_callback(MakeNewServiceDestructionCallback());
break;
}
case MathInterface::kGetMultiply: {
auto service = base::MakeRefCounted<MultiplyService>(value);
RETURN_IF_ERROR(out.WriteBinder(service->GetBinder()));
service->set_destruction_callback(MakeNewServiceDestructionCallback());
break;
}
default:
NOTREACHED();
}
return base::ok();
}
void WaitForAllServicesToBeDestroyed() { all_services_destroyed_.Wait(); }
private:
base::OnceClosure MakeNewServiceDestructionCallback() {
num_service_instances_.fetch_add(1, std::memory_order_relaxed);
return base::BindLambdaForTesting([this] {
if (num_service_instances_.fetch_sub(1, std::memory_order_relaxed) == 1) {
all_services_destroyed_.Signal();
}
});
}
~MathService() override = default;
std::atomic_int num_service_instances_{0};
base::WaitableEvent all_services_destroyed_;
};
TEST_F(BinderTest, BindersInTransactions) {
auto math_service = base::MakeRefCounted<MathService>();
MathInterface::Proxy math(math_service->GetBinder());
auto add2 = math.GetAdd(2);
auto multiply3 = math.GetMultiply(3);
EXPECT_EQ(8002, add2.Add(8000));
EXPECT_EQ(27000, multiply3.Multiply(9000));
add2.reset();
multiply3.reset();
math_service->WaitForAllServicesToBeDestroyed();
}
class BinderMultiprocessTest : public BinderTest {
public:
template <typename... Binders>
Process LaunchChild(const char* name, Binders&&... binders) {
CommandLine cmd = GetMultiProcessTestChildBaseCommandLine();
LaunchOptions options;
options.binders =
std::vector<BinderRef>({std::forward<Binders>(binders)...});
return SpawnMultiProcessTestChild(name, cmd, options);
}
bool JoinChild(const Process& child) {
int child_exit_code = -1;
WaitForMultiprocessTestChildExit(child, TestTimeouts::action_timeout(),
&child_exit_code);
return child_exit_code == 0;
}
};
// Helper to define child process logic such that EXPECT* macro failures will
// impact the process exit code and thereby percolate up to the parent test
// process when joining the child.
#define BINDER_TEST_CHILD_MAIN(name) \
class name##_Child { \
public: \
void Run(); \
}; \
MULTIPROCESS_TEST_MAIN(name) { \
name##_Child().Run(); \
return ::testing::Test::HasFailure() ? 1 : 0; \
} \
void name##_Child::Run()
TEST_F(BinderMultiprocessTest, PassBinder) {
auto add42 = base::MakeRefCounted<AddService>(42);
Process child = LaunchChild("PassBinder_Child", add42->GetBinder());
EXPECT_TRUE(JoinChild(child));
}
BINDER_TEST_CHILD_MAIN(PassBinder_Child) {
AddInterface::Proxy add42(TakeBinderFromParent(0));
EXPECT_EQ(47, add42.Add(5));
}
TEST_F(BinderMultiprocessTest, PassMultipleBinders) {
auto add100 = base::MakeRefCounted<AddService>(100);
auto multiply7 = base::MakeRefCounted<MultiplyService>(7);
Process child = LaunchChild("PassMultipleBinders_Child", add100->GetBinder(),
multiply7->GetBinder());
EXPECT_TRUE(JoinChild(child));
}
BINDER_TEST_CHILD_MAIN(PassMultipleBinders_Child) {
AddInterface::Proxy add100(TakeBinderFromParent(0));
MultiplyInterface::Proxy multiply7(TakeBinderFromParent(1));
EXPECT_EQ(105, add100.Add(5));
EXPECT_EQ(63, multiply7.Multiply(9));
}
TEST_F(BinderMultiprocessTest, BindersInTransactions) {
auto math = base::MakeRefCounted<MathService>();
Process child = LaunchChild("BindersInTransactions_Child", math->GetBinder());
math->WaitForAllServicesToBeDestroyed();
EXPECT_TRUE(JoinChild(child));
}
BINDER_TEST_CHILD_MAIN(BindersInTransactions_Child) {
MathInterface::Proxy math(TakeBinderFromParent(0));
auto add2 = math.GetAdd(2);
auto multiply3 = math.GetMultiply(3);
EXPECT_EQ(8002, add2.Add(8000));
EXPECT_EQ(27000, multiply3.Multiply(9000));
}
TEST_F(BinderMultiprocessTest, PassedBinderLifetime) {
auto service = base::MakeRefCounted<AddService>(15);
bool is_destroyed = false;
base::WaitableEvent destruction;
service->set_destruction_callback(base::BindLambdaForTesting([&] {
is_destroyed = true;
destruction.Signal();
}));
auto binder = service->GetBinder();
service.reset();
EXPECT_FALSE(is_destroyed);
Process child = LaunchChild("PassedBinderLifetime_Child", std::move(binder));
EXPECT_TRUE(JoinChild(child));
destruction.Wait();
EXPECT_TRUE(is_destroyed);
}
BINDER_TEST_CHILD_MAIN(PassedBinderLifetime_Child) {
AddInterface::Proxy add15(TakeBinderFromParent(0));
EXPECT_EQ(18, add15.Add(3));
}
DEFINE_BINDER_CLASS(BinderSinkClass);
class BinderSink : public SupportsBinder<BinderSinkClass> {
public:
using Callback = RepeatingCallback<void(BinderRef)>;
explicit BinderSink(BinderMultiprocessTest& test) : test_(test) {}
template <typename Fn>
Process LaunchChildAndWaitForBinder(const char* child_name, Fn fn) {
callback_ = BindLambdaForTesting(fn);
Process process = test_->LaunchChild(child_name, GetBinder());
called_.Wait();
return process;
}
void WaitForDisconnect() { disconnected_.Wait(); }
// SupportsBinder<BinderSinkClass>:
BinderStatusOr<void> OnBinderTransaction(transaction_code_t code,
const ParcelReader& reader,
const ParcelWriter& out) override {
ASSIGN_OR_RETURN(auto binder, reader.ReadBinder());
callback_.Run(std::move(binder));
called_.Signal();
return base::ok();
}
void OnBinderDestroyed() override { disconnected_.Signal(); }
private:
~BinderSink() override = default;
raw_ref<BinderMultiprocessTest> test_;
BinderRef child_binder_;
Callback callback_;
WaitableEvent called_;
WaitableEvent disconnected_;
};
TEST_F(BinderMultiprocessTest, AssociateValid) {
auto sink = base::MakeRefCounted<BinderSink>(*this);
Process child = sink->LaunchChildAndWaitForBinder(
"Associate_Child", [](BinderRef binder) {
EXPECT_TRUE(
binder.AssociateWithClass(AddInterface::Class::GetBinderClass()));
});
EXPECT_TRUE(JoinChild(child));
}
TEST_F(BinderMultiprocessTest, AssociateInvalid) {
auto sink = base::MakeRefCounted<BinderSink>(*this);
Process child = sink->LaunchChildAndWaitForBinder(
"Associate_Child", [](BinderRef binder) {
EXPECT_FALSE(binder.AssociateWithClass(
MultiplyInterface::Class::GetBinderClass()));
});
EXPECT_TRUE(JoinChild(child));
}
BINDER_TEST_CHILD_MAIN(Associate_Child) {
auto sink = BinderSinkClass::AdoptBinderRef(TakeBinderFromParent(0));
auto service = base::MakeRefCounted<AddService>(15);
std::ignore = sink.Transact(42, [&service](ParcelWriter in) {
return in.WriteBinder(service->GetBinder());
});
}
TEST_F(BinderMultiprocessTest, AssociateDestroyed) {
auto sink = base::MakeRefCounted<BinderSink>(*this);
BinderRef child_binder;
Process child = sink->LaunchChildAndWaitForBinder(
"AssociateDestroyed_Child",
[&](BinderRef binder) { child_binder = std::move(binder); });
sink->WaitForDisconnect();
// Though the class would be correct, association should fail because the
// child process is already dead and the class descriptor can't be validated.
EXPECT_FALSE(
child_binder.AssociateWithClass(AddInterface::Class::GetBinderClass()));
}
BINDER_TEST_CHILD_MAIN(AssociateDestroyed_Child) {
auto sink = BinderSinkClass::AdoptBinderRef(TakeBinderFromParent(0));
auto service = base::MakeRefCounted<AddService>(15);
std::ignore = sink.Transact(42, [&](ParcelWriter in) {
return in.WriteBinder(service->GetBinder());
});
Process::TerminateCurrentProcessImmediately(0);
}
} // namespace
} // namespace base::android