/* * 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 #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 session; sp root; // Trusty defines its own socket APIs in trusty_ipc.h but doesn't include // sockaddr types. #ifndef __TRUSTY__ sockaddr_storage addr; socklen_t addrLen; #endif }; // client session objects associated with other process // each one represents a separate session std::vector sessions; virtual ~ProcessSession() = 0; // If the process exits with a status, run the given callback on that value. virtual void setCustomExitStatusCheck(std::function 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 proc; // pre-fetched root object (for first session) sp rootBinder; // pre-casted root object (for first session) sp 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 proc) : proc(std::move(proc)){}; BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; ~BinderRpcTestProcessSession() { if (!expectAlreadyShutdown) { EXPECT_NE(nullptr, rootIface); if (rootIface == nullptr) return; std::vector 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 { 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(ret.rootBinder); return ret; } static std::string PrintParamInfo(const testing::TestParamInfo& 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 newFactory(RpcSecurity rpcSecurity); std::unique_ptr createRpcTestSocketServerProcessEtc( const BinderRpcOptions& options); }; } // namespace android