blob: 2c9646b30e433ecf6e1e88781c137a029b07d6c5 [file] [log] [blame]
/*
* Copyright (C) 2022 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.
*/
#pragma once
#include <gtest/gtest.h>
#include "binderRpcTestCommon.h"
#define EXPECT_OK(status) \
do { \
android::binder::Status stat = (status); \
EXPECT_TRUE(stat.isOk()) << stat; \
} while (false)
namespace android {
// Abstract base class with a virtual destructor that handles the
// ownership of a process session for BinderRpcTestSession below
class ProcessSession {
public:
struct SessionInfo {
sp<RpcSession> session;
sp<IBinder> root;
};
// client session objects associated with other process
// each one represents a separate session
std::vector<SessionInfo> sessions;
virtual ~ProcessSession() = 0;
// If the process exits with a status, run the given callback on that value.
virtual void setCustomExitStatusCheck(std::function<void(int wstatus)> f) = 0;
// Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead.
virtual void terminate() = 0;
};
// Process session where the process hosts IBinderRpcTest, the server used
// for most testing here
struct BinderRpcTestProcessSession {
std::unique_ptr<ProcessSession> proc;
// pre-fetched root object (for first session)
sp<IBinder> rootBinder;
// pre-casted root object (for first session)
sp<IBinderRpcTest> rootIface;
// whether session should be invalidated by end of run
bool expectAlreadyShutdown = false;
// TODO(b/271830568): fix this in binderRpcTest, we always use the first session to cause the
// remote process to shutdown. Normally, when we shutdown, the default in the destructor is to
// check that there are no leaks and shutdown. However, when there are incoming threadpools,
// there will be a few extra binder threads there, so we can't shutdown the server. We should
// consider an alternative way of doing the test so that we don't need this, some ideas, such as
// program in understanding of incoming threadpool into the destructor so that (e.g.
// intelligently wait for sessions to shutdown now that they will do this)
void forceShutdown() {
if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
}
EXPECT_TRUE(proc->sessions.at(0).session->shutdownAndWait(true));
expectAlreadyShutdown = true;
}
BinderRpcTestProcessSession(std::unique_ptr<ProcessSession> proc) : proc(std::move(proc)){};
BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
~BinderRpcTestProcessSession() {
if (!expectAlreadyShutdown) {
EXPECT_NE(nullptr, rootIface);
if (rootIface == nullptr) return;
std::vector<int32_t> remoteCounts;
// calling over any sessions counts across all sessions
EXPECT_OK(rootIface->countBinders(&remoteCounts));
EXPECT_EQ(remoteCounts.size(), proc->sessions.size());
for (auto remoteCount : remoteCounts) {
EXPECT_EQ(remoteCount, 1);
}
// even though it is on another thread, shutdown races with
// the transaction reply being written
if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
}
}
rootIface = nullptr;
rootBinder = nullptr;
}
};
struct BinderRpcParam {
SocketType type;
RpcSecurity security;
uint32_t clientVersion;
uint32_t serverVersion;
bool singleThreaded;
bool noKernel;
};
class BinderRpc : public ::testing::TestWithParam<BinderRpcParam> {
public:
// TODO: avoid unnecessary layer of indirection
SocketType socketType() const { return GetParam().type; }
RpcSecurity rpcSecurity() const { return GetParam().security; }
uint32_t clientVersion() const { return GetParam().clientVersion; }
uint32_t serverVersion() const { return GetParam().serverVersion; }
bool serverSingleThreaded() const { return GetParam().singleThreaded; }
bool noKernel() const { return GetParam().noKernel; }
bool clientOrServerSingleThreaded() const {
return !kEnableRpcThreads || serverSingleThreaded();
}
// Whether the test params support sending FDs in parcels.
bool supportsFdTransport() const {
if (socketType() == SocketType::TIPC) {
// Trusty does not support file descriptors yet
return false;
}
return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
(socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
socketType() == SocketType::UNIX_BOOTSTRAP ||
socketType() == SocketType::UNIX_RAW);
}
void SetUp() override {
if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) {
GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport";
}
}
BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
BinderRpcTestProcessSession ret(createRpcTestSocketServerProcessEtc(options));
ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root;
ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
return ret;
}
static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
auto ret = PrintToString(info.param.type) + "_" +
newFactory(info.param.security)->toCString() + "_clientV" +
std::to_string(info.param.clientVersion) + "_serverV" +
std::to_string(info.param.serverVersion);
if (info.param.singleThreaded) {
ret += "_single_threaded";
} else {
ret += "_multi_threaded";
}
if (info.param.noKernel) {
ret += "_no_kernel";
} else {
ret += "_with_kernel";
}
return ret;
}
protected:
static std::unique_ptr<RpcTransportCtxFactory> newFactory(RpcSecurity rpcSecurity);
std::unique_ptr<ProcessSession> createRpcTestSocketServerProcessEtc(
const BinderRpcOptions& options);
};
} // namespace android