Merge "SF: refactor renderScreenImpl to use CompositionEngine"
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index ebb7891..34ea759 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1956,7 +1956,7 @@
join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
debuggable, boot_complete, for_restore, target_sdk_version,
enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
- compilation_reason);
+ background_job_compile, compilation_reason);
bool cancelled = false;
pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 51c4589..4221a3a 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -81,6 +81,7 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
bool use_jitzygote,
+ bool background_job_compile,
const char* compilation_reason) {
PrepareBootImageFlags(use_jitzygote);
@@ -92,7 +93,8 @@
debuggable, target_sdk_version, enable_hidden_api_checks,
generate_compact_dex, compilation_reason);
- PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore);
+ PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore,
+ background_job_compile);
const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
@@ -296,7 +298,8 @@
}
void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
- bool for_restore) {
+ bool for_restore,
+ bool background_job_compile) {
// CPU set
{
std::string cpu_set_format = "--cpu-set=%s";
@@ -306,7 +309,12 @@
"dalvik.vm.restore-dex2oat-cpu-set",
"dalvik.vm.dex2oat-cpu-set",
cpu_set_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))
+ : (background_job_compile
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.background-dex2oat-cpu-set",
+ "dalvik.vm.dex2oat-cpu-set",
+ cpu_set_format)
+ : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)))
: MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format);
AddArg(dex2oat_cpu_set_arg);
}
@@ -320,7 +328,12 @@
"dalvik.vm.restore-dex2oat-threads",
"dalvik.vm.dex2oat-threads",
threads_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
+ : (background_job_compile
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.background-dex2oat-threads",
+ "dalvik.vm.dex2oat-threads",
+ threads_format)
+ : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)))
: MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
AddArg(dex2oat_threads_arg);
}
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 559244f..c13e1f1 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -51,6 +51,7 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
bool use_jitzygote,
+ bool background_job_compile,
const char* compilation_reason);
void Exec(int exit_code);
@@ -76,7 +77,9 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
const char* compilation_reason);
- void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore);
+ void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
+ bool for_restore,
+ bool background_job_compile);
virtual std::string GetProperty(const std::string& key, const std::string& default_value);
virtual bool GetBoolProperty(const std::string& key, bool default_value);
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 2a8135a..304ba7b 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -115,6 +115,7 @@
bool enable_hidden_api_checks = false;
bool generate_compact_dex = true;
bool use_jitzygote = false;
+ bool background_job_compile = false;
const char* compilation_reason = nullptr;
};
@@ -259,6 +260,7 @@
args->enable_hidden_api_checks,
args->generate_compact_dex,
args->use_jitzygote,
+ args->background_job_compile,
args->compilation_reason);
runner.Exec(/*exit_code=*/ 0);
}
@@ -375,6 +377,30 @@
VerifyExpectedFlags();
}
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground) {
+ setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", "1,3");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,3");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground_Backup) {
+ setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", "");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) {
setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2");
setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3");
@@ -481,6 +507,30 @@
VerifyExpectedFlags();
}
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground) {
+ setSystemProperty("dalvik.vm.background-dex2oat-threads", "2");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground_Backup) {
+ setSystemProperty("dalvik.vm.background-dex2oat-threads", "");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "3");
+ VerifyExpectedFlags();
+}
+
TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) {
setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4");
setSystemProperty("dalvik.vm.dex2oat-threads", "5");
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 92d132f..03e4a23 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -232,6 +232,7 @@
srcs: [
"binderRpcTest.cpp",
"binderRpcTestCommon.cpp",
+ "binderRpcUniversalTests.cpp",
],
test_suites: ["general-tests"],
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 5d5b530..004dea3 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -15,7 +15,6 @@
*/
#include <android-base/stringprintf.h>
-#include <gtest/gtest.h>
#include <chrono>
#include <cstdlib>
@@ -45,29 +44,6 @@
constexpr bool kEnableSharedLibs = true;
#endif
-static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
- RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
-
-TEST(BinderRpcParcel, EntireParcelFormatted) {
- Parcel p;
- p.writeInt32(3);
-
- EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "format must be set before data is written");
-}
-
-TEST(BinderRpc, CannotUseNextWireVersion) {
- auto session = RpcSession::make();
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
-}
-
-TEST(BinderRpc, CanUseExperimentalWireVersion) {
- auto session = RpcSession::make();
- EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
-}
-
static std::string WaitStatusToString(int wstatus) {
if (WIFEXITED(wstatus)) {
return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
@@ -362,380 +338,6 @@
return ret;
}
-TEST_P(BinderRpc, Ping) {
- auto proc = createRpcTestSocketServerProcess({});
- ASSERT_NE(proc.rootBinder, nullptr);
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-}
-
-TEST_P(BinderRpc, GetInterfaceDescriptor) {
- auto proc = createRpcTestSocketServerProcess({});
- ASSERT_NE(proc.rootBinder, nullptr);
- EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
-}
-
-TEST_P(BinderRpc, MultipleSessions) {
- if (serverSingleThreaded()) {
- // Tests with multiple sessions require a multi-threaded service,
- // but work fine on a single-threaded client
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
- for (auto session : proc.proc->sessions) {
- ASSERT_NE(nullptr, session.root);
- EXPECT_EQ(OK, session.root->pingBinder());
- }
-}
-
-TEST_P(BinderRpc, SeparateRootObject) {
- if (serverSingleThreaded()) {
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- SocketType type = std::get<0>(GetParam());
- if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
- type == SocketType::UNIX_BOOTSTRAP) {
- // we can't get port numbers for unix sockets
- return;
- }
-
- auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
-
- int port1 = 0;
- EXPECT_OK(proc.rootIface->getClientPort(&port1));
-
- sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
- int port2;
- EXPECT_OK(rootIface2->getClientPort(&port2));
-
- // we should have a different IBinderRpcTest object created for each
- // session, because we use setPerSessionRootObject
- EXPECT_NE(port1, port2);
-}
-
-TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
- auto proc = createRpcTestSocketServerProcess({});
- Parcel data;
- Parcel reply;
- EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
-}
-
-TEST_P(BinderRpc, AppendSeparateFormats) {
- auto proc1 = createRpcTestSocketServerProcess({});
- auto proc2 = createRpcTestSocketServerProcess({});
-
- Parcel pRaw;
-
- Parcel p1;
- p1.markForBinder(proc1.rootBinder);
- p1.writeInt32(3);
-
- EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize()));
- EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
-
- Parcel p2;
- p2.markForBinder(proc2.rootBinder);
- p2.writeInt32(7);
-
- EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
- EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
-}
-
-TEST_P(BinderRpc, UnknownTransaction) {
- auto proc = createRpcTestSocketServerProcess({});
- Parcel data;
- data.markForBinder(proc.rootBinder);
- Parcel reply;
- EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
-}
-
-TEST_P(BinderRpc, SendSomethingOneway) {
- auto proc = createRpcTestSocketServerProcess({});
- EXPECT_OK(proc.rootIface->sendString("asdf"));
-}
-
-TEST_P(BinderRpc, SendAndGetResultBack) {
- auto proc = createRpcTestSocketServerProcess({});
- std::string doubled;
- EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
- EXPECT_EQ("cool cool ", doubled);
-}
-
-TEST_P(BinderRpc, SendAndGetResultBackBig) {
- auto proc = createRpcTestSocketServerProcess({});
- std::string single = std::string(1024, 'a');
- std::string doubled;
- EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
- EXPECT_EQ(single + single, doubled);
-}
-
-TEST_P(BinderRpc, InvalidNullBinderReturn) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
-}
-
-TEST_P(BinderRpc, CallMeBack) {
- auto proc = createRpcTestSocketServerProcess({});
-
- int32_t pingResult;
- EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
- EXPECT_EQ(OK, pingResult);
-
- EXPECT_EQ(0, MyBinderRpcSession::gNum);
-}
-
-TEST_P(BinderRpc, RepeatBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> inBinder = new MyBinderRpcSession("foo");
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
- EXPECT_EQ(inBinder, outBinder);
-
- wp<IBinder> weak = inBinder;
- inBinder = nullptr;
- outBinder = nullptr;
-
- // Force reading a reply, to process any pending dec refs from the other
- // process (the other process will process dec refs there before processing
- // the ping here).
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
- EXPECT_EQ(nullptr, weak.promote());
-
- EXPECT_EQ(0, MyBinderRpcSession::gNum);
-}
-
-TEST_P(BinderRpc, RepeatTheirBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
-
- sp<IBinder> inBinder = IInterface::asBinder(session);
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
- EXPECT_EQ(inBinder, outBinder);
-
- wp<IBinder> weak = inBinder;
- session = nullptr;
- inBinder = nullptr;
- outBinder = nullptr;
-
- // Force reading a reply, to process any pending dec refs from the other
- // process (the other process will process dec refs there before processing
- // the ping here).
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
- EXPECT_EQ(nullptr, weak.promote());
-}
-
-TEST_P(BinderRpc, RepeatBinderNull) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
- EXPECT_EQ(nullptr, outBinder);
-}
-
-TEST_P(BinderRpc, HoldBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- IBinder* ptr = nullptr;
- {
- sp<IBinder> binder = new BBinder();
- ptr = binder.get();
- EXPECT_OK(proc.rootIface->holdBinder(binder));
- }
-
- sp<IBinder> held;
- EXPECT_OK(proc.rootIface->getHeldBinder(&held));
-
- EXPECT_EQ(held.get(), ptr);
-
- // stop holding binder, because we test to make sure references are cleaned
- // up
- EXPECT_OK(proc.rootIface->holdBinder(nullptr));
- // and flush ref counts
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-}
-
-// START TESTS FOR LIMITATIONS OF SOCKET BINDER
-// These are behavioral differences form regular binder, where certain usecases
-// aren't supported.
-
-TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
- auto proc1 = createRpcTestSocketServerProcess({});
- auto proc2 = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
-}
-
-TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
- if (serverSingleThreaded()) {
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc.rootIface->repeatBinder(proc.proc->sessions.at(1).root, &outBinder)
- .transactionError());
-}
-
-TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
- if (!kEnableKernelIpc || noKernel()) {
- GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
- "at build time.";
- }
-
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
-}
-
-TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
- if (!kEnableKernelIpc || noKernel()) {
- GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
- "at build time.";
- }
-
- auto proc = createRpcTestSocketServerProcess({});
-
- // for historical reasons, IServiceManager interface only returns the
- // exception code
- EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
- defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
-}
-
-// END TESTS FOR LIMITATIONS OF SOCKET BINDER
-
-TEST_P(BinderRpc, RepeatRootObject) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
- EXPECT_EQ(proc.rootBinder, outBinder);
-}
-
-TEST_P(BinderRpc, NestedTransactions) {
- auto proc = createRpcTestSocketServerProcess({
- // Enable FD support because it uses more stack space and so represents
- // something closer to a worst case scenario.
- .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
- .serverSupportedFileDescriptorTransportModes =
- {RpcSession::FileDescriptorTransportMode::UNIX},
- });
-
- auto nastyNester = sp<MyBinderRpcTest>::make();
- EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
-
- wp<IBinder> weak = nastyNester;
- nastyNester = nullptr;
- EXPECT_EQ(nullptr, weak.promote());
-}
-
-TEST_P(BinderRpc, SameBinderEquality) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> a;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
-
- sp<IBinder> b;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
-
- EXPECT_EQ(a, b);
-}
-
-TEST_P(BinderRpc, SameBinderEqualityWeak) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> a;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
- wp<IBinder> weak = a;
- a = nullptr;
-
- sp<IBinder> b;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
-
- // this is the wrong behavior, since BpBinder
- // doesn't implement onIncStrongAttempted
- // but make sure there is no crash
- EXPECT_EQ(nullptr, weak.promote());
-
- GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
-
- // In order to fix this:
- // - need to have incStrongAttempted reflected across IPC boundary (wait for
- // response to promote - round trip...)
- // - sendOnLastWeakRef, to delete entries out of RpcState table
- EXPECT_EQ(b, weak.promote());
-}
-
-#define expectSessions(expected, iface) \
- do { \
- int session; \
- EXPECT_OK((iface)->getNumOpenSessions(&session)); \
- EXPECT_EQ(expected, session); \
- } while (false)
-
-TEST_P(BinderRpc, SingleSession) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
- std::string out;
- EXPECT_OK(session->getName(&out));
- EXPECT_EQ("aoeu", out);
-
- expectSessions(1, proc.rootIface);
- session = nullptr;
- expectSessions(0, proc.rootIface);
-}
-
-TEST_P(BinderRpc, ManySessions) {
- auto proc = createRpcTestSocketServerProcess({});
-
- std::vector<sp<IBinderRpcSession>> sessions;
-
- for (size_t i = 0; i < 15; i++) {
- expectSessions(i, proc.rootIface);
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
- sessions.push_back(session);
- }
- expectSessions(sessions.size(), proc.rootIface);
- for (size_t i = 0; i < sessions.size(); i++) {
- std::string out;
- EXPECT_OK(sessions.at(i)->getName(&out));
- EXPECT_EQ(std::to_string(i), out);
- }
- expectSessions(sessions.size(), proc.rootIface);
-
- while (!sessions.empty()) {
- sessions.pop_back();
- expectSessions(sessions.size(), proc.rootIface);
- }
- expectSessions(0, proc.rootIface);
-}
-
-size_t epochMillis() {
- using std::chrono::duration_cast;
- using std::chrono::milliseconds;
- using std::chrono::seconds;
- using std::chrono::system_clock;
- return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
-}
-
TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
if (clientOrServerSingleThreaded()) {
GTEST_SKIP() << "This test requires multiple threads";
@@ -873,20 +475,6 @@
saturateThreadPool(kNumServerThreads, proc.rootIface);
}
-TEST_P(BinderRpc, OnewayCallDoesNotWait) {
- constexpr size_t kReallyLongTimeMs = 100;
- constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
-
- auto proc = createRpcTestSocketServerProcess({});
-
- size_t epochMsBefore = epochMillis();
-
- EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
-
- size_t epochMsAfter = epochMillis();
- EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
-}
-
TEST_P(BinderRpc, OnewayCallQueueingWithFds) {
if (!supportsFdTransport()) {
GTEST_SKIP() << "Would fail trivially (which is tested elsewhere)";
@@ -1003,65 +591,6 @@
proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
}
-TEST_P(BinderRpc, Callbacks) {
- const static std::string kTestString = "good afternoon!";
-
- for (bool callIsOneway : {true, false}) {
- for (bool callbackIsOneway : {true, false}) {
- for (bool delayed : {true, false}) {
- if (clientOrServerSingleThreaded() &&
- (callIsOneway || callbackIsOneway || delayed)) {
- // we have no incoming connections to receive the callback
- continue;
- }
-
- size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1;
- auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1,
- .numSessions = 1,
- .numIncomingConnections = numIncomingConnections});
- auto cb = sp<MyBinderRpcCallback>::make();
-
- if (callIsOneway) {
- EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed,
- kTestString));
- } else {
- EXPECT_OK(
- proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString));
- }
-
- // if both transactions are synchronous and the response is sent back on the
- // same thread, everything should have happened in a nested call. Otherwise,
- // the callback will be processed on another thread.
- if (callIsOneway || callbackIsOneway || delayed) {
- using std::literals::chrono_literals::operator""s;
- RpcMutexUniqueLock _l(cb->mMutex);
- cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
- }
-
- EXPECT_EQ(cb->mValues.size(), 1)
- << "callIsOneway: " << callIsOneway
- << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
- if (cb->mValues.empty()) continue;
- EXPECT_EQ(cb->mValues.at(0), kTestString)
- << "callIsOneway: " << callIsOneway
- << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
-
- // since we are severing the connection, we need to go ahead and
- // tell the server to shutdown and exit so that waitpid won't hang
- if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
- EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
- }
-
- // since this session has an incoming connection w/ a threadpool, we
- // need to manually shut it down
- EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
- proc.expectAlreadyShutdown = true;
- }
- }
- }
-}
-
TEST_P(BinderRpc, SingleDeathRecipient) {
if (clientOrServerSingleThreaded()) {
GTEST_SKIP() << "This test requires multiple threads";
@@ -1178,14 +707,6 @@
proc.expectAlreadyShutdown = true;
}
-TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
- auto proc = createRpcTestSocketServerProcess({});
- auto cb = sp<MyBinderRpcCallback>::make();
-
- Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
- EXPECT_EQ(WOULD_BLOCK, status.transactionError());
-}
-
TEST_P(BinderRpc, Die) {
for (bool doDeathCleanup : {true, false}) {
auto proc = createRpcTestSocketServerProcess({});
@@ -1431,16 +952,6 @@
ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
}
-TEST_P(BinderRpc, AidlDelegatorTest) {
- auto proc = createRpcTestSocketServerProcess({});
- auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface);
- ASSERT_NE(nullptr, myDelegator);
-
- std::string doubled;
- EXPECT_OK(myDelegator->doubleString("cool ", &doubled));
- EXPECT_EQ("cool cool ", doubled);
-}
-
static bool testSupportVsockLoopback() {
// We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 823bbf6..dc7d264 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -91,6 +91,14 @@
}
}
+static inline size_t epochMillis() {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::seconds;
+ using std::chrono::system_clock;
+ return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+}
+
struct BinderRpcOptions {
size_t numThreads = 1;
size_t numSessions = 1;
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
new file mode 100644
index 0000000..1e8d93d
--- /dev/null
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -0,0 +1,513 @@
+/*
+ * 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.
+ */
+
+#include <chrono>
+#include <cstdlib>
+#include <type_traits>
+
+#include "binderRpcTestCommon.h"
+#include "binderRpcTestFixture.h"
+
+using namespace std::chrono_literals;
+using namespace std::placeholders;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+
+namespace android {
+
+static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
+ RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+
+TEST(BinderRpcParcel, EntireParcelFormatted) {
+ Parcel p;
+ p.writeInt32(3);
+
+ EXPECT_DEATH_IF_SUPPORTED(p.markForBinder(sp<BBinder>::make()),
+ "format must be set before data is written");
+}
+
+TEST(BinderRpc, CannotUseNextWireVersion) {
+ auto session = RpcSession::make();
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
+}
+
+TEST(BinderRpc, CanUseExperimentalWireVersion) {
+ auto session = RpcSession::make();
+ EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
+}
+
+TEST_P(BinderRpc, Ping) {
+ auto proc = createRpcTestSocketServerProcess({});
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+TEST_P(BinderRpc, GetInterfaceDescriptor) {
+ auto proc = createRpcTestSocketServerProcess({});
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
+}
+
+TEST_P(BinderRpc, MultipleSessions) {
+ if (serverSingleThreaded()) {
+ // Tests with multiple sessions require a multi-threaded service,
+ // but work fine on a single-threaded client
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
+ for (auto session : proc.proc->sessions) {
+ ASSERT_NE(nullptr, session.root);
+ EXPECT_EQ(OK, session.root->pingBinder());
+ }
+}
+
+TEST_P(BinderRpc, SeparateRootObject) {
+ if (serverSingleThreaded()) {
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ SocketType type = std::get<0>(GetParam());
+ if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
+ type == SocketType::UNIX_BOOTSTRAP) {
+ // we can't get port numbers for unix sockets
+ return;
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
+
+ int port1 = 0;
+ EXPECT_OK(proc.rootIface->getClientPort(&port1));
+
+ sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
+ int port2;
+ EXPECT_OK(rootIface2->getClientPort(&port2));
+
+ // we should have a different IBinderRpcTest object created for each
+ // session, because we use setPerSessionRootObject
+ EXPECT_NE(port1, port2);
+}
+
+TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
+ auto proc = createRpcTestSocketServerProcess({});
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
+}
+
+TEST_P(BinderRpc, AppendSeparateFormats) {
+ auto proc1 = createRpcTestSocketServerProcess({});
+ auto proc2 = createRpcTestSocketServerProcess({});
+
+ Parcel pRaw;
+
+ Parcel p1;
+ p1.markForBinder(proc1.rootBinder);
+ p1.writeInt32(3);
+
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize()));
+ EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
+
+ Parcel p2;
+ p2.markForBinder(proc2.rootBinder);
+ p2.writeInt32(7);
+
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
+ EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
+}
+
+TEST_P(BinderRpc, UnknownTransaction) {
+ auto proc = createRpcTestSocketServerProcess({});
+ Parcel data;
+ data.markForBinder(proc.rootBinder);
+ Parcel reply;
+ EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
+}
+
+TEST_P(BinderRpc, SendSomethingOneway) {
+ auto proc = createRpcTestSocketServerProcess({});
+ EXPECT_OK(proc.rootIface->sendString("asdf"));
+}
+
+TEST_P(BinderRpc, SendAndGetResultBack) {
+ auto proc = createRpcTestSocketServerProcess({});
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
+TEST_P(BinderRpc, SendAndGetResultBackBig) {
+ auto proc = createRpcTestSocketServerProcess({});
+ std::string single = std::string(1024, 'a');
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
+ EXPECT_EQ(single + single, doubled);
+}
+
+TEST_P(BinderRpc, InvalidNullBinderReturn) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
+}
+
+TEST_P(BinderRpc, CallMeBack) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ int32_t pingResult;
+ EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
+ EXPECT_EQ(OK, pingResult);
+
+ EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST_P(BinderRpc, RepeatBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> inBinder = new MyBinderRpcSession("foo");
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+ EXPECT_EQ(inBinder, outBinder);
+
+ wp<IBinder> weak = inBinder;
+ inBinder = nullptr;
+ outBinder = nullptr;
+
+ // Force reading a reply, to process any pending dec refs from the other
+ // process (the other process will process dec refs there before processing
+ // the ping here).
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ EXPECT_EQ(nullptr, weak.promote());
+
+ EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST_P(BinderRpc, RepeatTheirBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+
+ sp<IBinder> inBinder = IInterface::asBinder(session);
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+ EXPECT_EQ(inBinder, outBinder);
+
+ wp<IBinder> weak = inBinder;
+ session = nullptr;
+ inBinder = nullptr;
+ outBinder = nullptr;
+
+ // Force reading a reply, to process any pending dec refs from the other
+ // process (the other process will process dec refs there before processing
+ // the ping here).
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST_P(BinderRpc, RepeatBinderNull) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
+ EXPECT_EQ(nullptr, outBinder);
+}
+
+TEST_P(BinderRpc, HoldBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ IBinder* ptr = nullptr;
+ {
+ sp<IBinder> binder = new BBinder();
+ ptr = binder.get();
+ EXPECT_OK(proc.rootIface->holdBinder(binder));
+ }
+
+ sp<IBinder> held;
+ EXPECT_OK(proc.rootIface->getHeldBinder(&held));
+
+ EXPECT_EQ(held.get(), ptr);
+
+ // stop holding binder, because we test to make sure references are cleaned
+ // up
+ EXPECT_OK(proc.rootIface->holdBinder(nullptr));
+ // and flush ref counts
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+// START TESTS FOR LIMITATIONS OF SOCKET BINDER
+// These are behavioral differences form regular binder, where certain usecases
+// aren't supported.
+
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
+ auto proc1 = createRpcTestSocketServerProcess({});
+ auto proc2 = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
+}
+
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+ if (serverSingleThreaded()) {
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(proc.proc->sessions.at(1).root, &outBinder)
+ .transactionError());
+}
+
+TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
+}
+
+TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ // for historical reasons, IServiceManager interface only returns the
+ // exception code
+ EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
+ defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
+}
+
+// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+
+TEST_P(BinderRpc, RepeatRootObject) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
+ EXPECT_EQ(proc.rootBinder, outBinder);
+}
+
+TEST_P(BinderRpc, NestedTransactions) {
+ auto proc = createRpcTestSocketServerProcess({
+ // Enable FD support because it uses more stack space and so represents
+ // something closer to a worst case scenario.
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ auto nastyNester = sp<MyBinderRpcTest>::make();
+ EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
+
+ wp<IBinder> weak = nastyNester;
+ nastyNester = nullptr;
+ EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST_P(BinderRpc, SameBinderEquality) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+
+ sp<IBinder> b;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+ EXPECT_EQ(a, b);
+}
+
+TEST_P(BinderRpc, SameBinderEqualityWeak) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+ wp<IBinder> weak = a;
+ a = nullptr;
+
+ sp<IBinder> b;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+ // this is the wrong behavior, since BpBinder
+ // doesn't implement onIncStrongAttempted
+ // but make sure there is no crash
+ EXPECT_EQ(nullptr, weak.promote());
+
+ GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
+
+ // In order to fix this:
+ // - need to have incStrongAttempted reflected across IPC boundary (wait for
+ // response to promote - round trip...)
+ // - sendOnLastWeakRef, to delete entries out of RpcState table
+ EXPECT_EQ(b, weak.promote());
+}
+
+#define expectSessions(expected, iface) \
+ do { \
+ int session; \
+ EXPECT_OK((iface)->getNumOpenSessions(&session)); \
+ EXPECT_EQ(expected, session); \
+ } while (false)
+
+TEST_P(BinderRpc, SingleSession) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+ std::string out;
+ EXPECT_OK(session->getName(&out));
+ EXPECT_EQ("aoeu", out);
+
+ expectSessions(1, proc.rootIface);
+ session = nullptr;
+ expectSessions(0, proc.rootIface);
+}
+
+TEST_P(BinderRpc, ManySessions) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ std::vector<sp<IBinderRpcSession>> sessions;
+
+ for (size_t i = 0; i < 15; i++) {
+ expectSessions(i, proc.rootIface);
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
+ sessions.push_back(session);
+ }
+ expectSessions(sessions.size(), proc.rootIface);
+ for (size_t i = 0; i < sessions.size(); i++) {
+ std::string out;
+ EXPECT_OK(sessions.at(i)->getName(&out));
+ EXPECT_EQ(std::to_string(i), out);
+ }
+ expectSessions(sessions.size(), proc.rootIface);
+
+ while (!sessions.empty()) {
+ sessions.pop_back();
+ expectSessions(sessions.size(), proc.rootIface);
+ }
+ expectSessions(0, proc.rootIface);
+}
+
+TEST_P(BinderRpc, OnewayCallDoesNotWait) {
+ constexpr size_t kReallyLongTimeMs = 100;
+ constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ size_t epochMsBefore = epochMillis();
+
+ EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
+
+ size_t epochMsAfter = epochMillis();
+ EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
+}
+
+TEST_P(BinderRpc, Callbacks) {
+ const static std::string kTestString = "good afternoon!";
+
+ for (bool callIsOneway : {true, false}) {
+ for (bool callbackIsOneway : {true, false}) {
+ for (bool delayed : {true, false}) {
+ if (clientOrServerSingleThreaded() &&
+ (callIsOneway || callbackIsOneway || delayed)) {
+ // we have no incoming connections to receive the callback
+ continue;
+ }
+
+ size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1;
+ auto proc = createRpcTestSocketServerProcess(
+ {.numThreads = 1,
+ .numSessions = 1,
+ .numIncomingConnections = numIncomingConnections});
+ auto cb = sp<MyBinderRpcCallback>::make();
+
+ if (callIsOneway) {
+ EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed,
+ kTestString));
+ } else {
+ EXPECT_OK(
+ proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString));
+ }
+
+ // if both transactions are synchronous and the response is sent back on the
+ // same thread, everything should have happened in a nested call. Otherwise,
+ // the callback will be processed on another thread.
+ if (callIsOneway || callbackIsOneway || delayed) {
+ using std::literals::chrono_literals::operator""s;
+ RpcMutexUniqueLock _l(cb->mMutex);
+ cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
+ }
+
+ EXPECT_EQ(cb->mValues.size(), 1)
+ << "callIsOneway: " << callIsOneway
+ << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
+ if (cb->mValues.empty()) continue;
+ EXPECT_EQ(cb->mValues.at(0), kTestString)
+ << "callIsOneway: " << callIsOneway
+ << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
+
+ // since we are severing the connection, we need to go ahead and
+ // tell the server to shutdown and exit so that waitpid won't hang
+ if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
+
+ // since this session has an incoming connection w/ a threadpool, we
+ // need to manually shut it down
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
+ proc.expectAlreadyShutdown = true;
+ }
+ }
+ }
+}
+
+TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
+ auto proc = createRpcTestSocketServerProcess({});
+ auto cb = sp<MyBinderRpcCallback>::make();
+
+ Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
+ EXPECT_EQ(WOULD_BLOCK, status.transactionError());
+}
+
+TEST_P(BinderRpc, AidlDelegatorTest) {
+ auto proc = createRpcTestSocketServerProcess({});
+ auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface);
+ ASSERT_NE(nullptr, myDelegator);
+
+ std::string doubled;
+ EXPECT_OK(myDelegator->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 18ddfbc..9868c8e 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -313,26 +313,10 @@
return sPrimaryDisplayRotationFlags;
}
-std::string DisplayDevice::getDebugName() const {
- using namespace std::string_literals;
-
- std::string name = "Display "s + to_string(getId()) + " ("s;
-
- name += isVirtual() ? "virtual"s : "physical"s;
-
- if (isPrimary()) {
- name += ", primary"s;
- }
-
- return name + ", \""s + mDisplayName + "\")"s;
-}
-
void DisplayDevice::dump(utils::Dumper& dumper) const {
using namespace std::string_view_literals;
- dumper.dump({}, getDebugName());
-
- utils::Dumper::Indent indent(dumper);
+ dumper.dump("name"sv, '"' + mDisplayName + '"');
dumper.dump("powerMode"sv, mPowerMode);
if (mRefreshRateSelector) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 6c848bb..1602a71 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -248,10 +248,6 @@
// release HWC resources (if any) for removable displays
void disconnect();
- /* ------------------------------------------------------------------------
- * Debugging
- */
- std::string getDebugName() const;
void dump(utils::Dumper&) const;
private:
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 33a7bca..8238828 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -178,7 +178,8 @@
}
// For utils::Dumper ADL.
-namespace hardware::graphics::composer::V2_2 {
+namespace hardware::graphics::composer {
+namespace V2_2 {
inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) {
switch (mode) {
@@ -197,7 +198,9 @@
}
}
-} // namespace hardware::graphics::composer::V2_2
+} // namespace V2_2
+
+namespace V2_1 {
inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) {
switch (vsync) {
@@ -210,4 +213,6 @@
}
}
+} // namespace V2_1
+} // namespace hardware::graphics::composer
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index a6cd47b..1acf15a 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -668,6 +668,7 @@
StringAppendF(&result, " %s\n", toString(*connection).c_str());
}
}
+ result += '\n';
}
const char* EventThread::toCString(State state) {
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
index c233455..cb9bfe9 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
@@ -54,10 +54,9 @@
std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides(
bool supportsFrameRateOverrideByContent) {
std::lock_guard lock(mFrameRateOverridesLock);
+
std::vector<FrameRateOverride> overrides;
- overrides.reserve(std::max({mFrameRateOverridesFromGameManager.size(),
- mFrameRateOverridesFromBackdoor.size(),
- mFrameRateOverridesByContent.size()}));
+ overrides.reserve(maxOverridesCount());
for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
@@ -83,28 +82,34 @@
return overrides;
}
-void FrameRateOverrideMappings::dump(std::string& result) const {
- using base::StringAppendF;
+void FrameRateOverrideMappings::dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
std::lock_guard lock(mFrameRateOverridesLock);
- StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
- for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
- StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
- }
- StringAppendF(&result, "}\n");
+ const bool hasOverrides = maxOverridesCount() > 0;
+ dumper.dump("FrameRateOverrides"sv, hasOverrides ? ""sv : "none"sv);
- StringAppendF(&result, "Frame Rate Overrides (GameManager): {");
- for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
- StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
- }
- StringAppendF(&result, "}\n");
+ if (!hasOverrides) return;
- StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
- for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
- StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ dump(dumper, "setFrameRate"sv, mFrameRateOverridesByContent);
+ dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager);
+ dump(dumper, "Backdoor"sv, mFrameRateOverridesFromBackdoor);
+}
+
+void FrameRateOverrideMappings::dump(utils::Dumper& dumper, std::string_view name,
+ const UidToFrameRateOverride& overrides) const {
+ if (overrides.empty()) return;
+
+ utils::Dumper::Indent indent(dumper);
+ dumper.dump(name);
+ {
+ utils::Dumper::Indent indent(dumper);
+ for (const auto& [uid, frameRate] : overrides) {
+ using namespace std::string_view_literals;
+ dumper.dump("(uid, frameRate)"sv, uid, frameRate);
+ }
}
- StringAppendF(&result, "}\n");
}
bool FrameRateOverrideMappings::updateFrameRateOverridesByContent(
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
index 4185a4c..da0f276 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
@@ -23,7 +23,10 @@
#include <map>
#include <optional>
+#include "Utils/Dumper.h"
+
namespace android::scheduler {
+
class FrameRateOverrideMappings {
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
using UidToFrameRateOverride = std::map<uid_t, Fps>;
@@ -34,7 +37,6 @@
EXCLUDES(mFrameRateOverridesLock);
std::vector<FrameRateOverride> getAllFrameRateOverrides(bool supportsFrameRateOverrideByContent)
EXCLUDES(mFrameRateOverridesLock);
- void dump(std::string& result) const;
bool updateFrameRateOverridesByContent(const UidToFrameRateOverride& frameRateOverrides)
EXCLUDES(mFrameRateOverridesLock);
void setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride)
@@ -42,7 +44,17 @@
void setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride)
EXCLUDES(mFrameRateOverridesLock);
+ void dump(utils::Dumper&) const;
+
private:
+ size_t maxOverridesCount() const REQUIRES(mFrameRateOverridesLock) {
+ return std::max({mFrameRateOverridesByContent.size(),
+ mFrameRateOverridesFromGameManager.size(),
+ mFrameRateOverridesFromBackdoor.size()});
+ }
+
+ void dump(utils::Dumper&, std::string_view name, const UidToFrameRateOverride&) const;
+
// The frame rate override lists need their own mutex as they are being read
// by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
mutable std::mutex mFrameRateOverridesLock;
@@ -53,4 +65,5 @@
UidToFrameRateOverride mFrameRateOverridesFromBackdoor GUARDED_BY(mFrameRateOverridesLock);
UidToFrameRateOverride mFrameRateOverridesFromGameManager GUARDED_BY(mFrameRateOverridesLock);
};
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index b884dc8..7c9cedfa 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -275,7 +275,7 @@
std::string LayerHistory::dump() const {
std::lock_guard lock(mLock);
- return base::StringPrintf("LayerHistory{size=%zu, active=%zu}",
+ return base::StringPrintf("{size=%zu, active=%zu}",
mActiveLayerInfos.size() + mInactiveLayerInfos.size(),
mActiveLayerInfos.size());
}
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 3c8dc64..cd45bfd 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -179,11 +179,5 @@
}
}
-std::string OneShotTimer::dump() const {
- std::ostringstream stream;
- stream << mInterval.count() << " ms";
- return stream.str();
-}
-
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 2017c31..f95646c 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -23,6 +23,7 @@
#include "../Clock.h"
#include <android-base/thread_annotations.h>
+#include <scheduler/Time.h>
namespace android {
namespace scheduler {
@@ -42,6 +43,8 @@
std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
~OneShotTimer();
+ Duration interval() const { return mInterval; }
+
// Initializes and turns on the idle timer.
void start();
// Stops the idle timer and any held resources.
@@ -49,8 +52,6 @@
// Resets the wakeup time and fires the reset callback.
void reset();
- std::string dump() const;
-
private:
// Enum to track in what state is the timer.
enum class TimerState {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index c913891..8d4ea05 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -1157,20 +1157,15 @@
dumper.dump("frameRateOverrideConfig"sv, *ftl::enum_name(mFrameRateOverrideConfig));
- std::string idleTimer;
- if (mIdleTimer) {
- idleTimer = mIdleTimer->dump();
- } else {
- idleTimer = "off"sv;
+ dumper.dump("idleTimer"sv);
+ {
+ utils::Dumper::Indent indent(dumper);
+ dumper.dump("interval"sv, mIdleTimer.transform(&OneShotTimer::interval));
+ dumper.dump("controller"sv,
+ mConfig.kernelIdleTimerController
+ .and_then(&ftl::enum_name<KernelIdleTimerController>)
+ .value_or("Platform"sv));
}
-
- if (const auto controller = mConfig.kernelIdleTimerController) {
- base::StringAppendF(&idleTimer, " (kernel via %s)", ftl::enum_string(*controller).c_str());
- } else {
- idleTimer += " (platform)"sv;
- }
-
- dumper.dump("idleTimer"sv, idleTimer);
}
std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index abbd304..0e80817 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -18,12 +18,12 @@
#include <algorithm>
#include <numeric>
-#include <optional>
#include <type_traits>
#include <utility>
#include <variant>
#include <ftl/concat.h>
+#include <ftl/optional.h>
#include <gui/DisplayEventReceiver.h>
#include <scheduler/Fps.h>
@@ -276,7 +276,7 @@
// The controller representing how the kernel idle timer will be configured
// either on the HWC api or sysprop.
- std::optional<KernelIdleTimerController> kernelIdleTimerController;
+ ftl::Optional<KernelIdleTimerController> kernelIdleTimerController;
};
RefreshRateSelector(
@@ -474,7 +474,7 @@
std::mutex mIdleTimerCallbacksMutex;
std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
// Used to detect (lack of) frame activity.
- std::optional<scheduler::OneShotTimer> mIdleTimer;
+ ftl::Optional<scheduler::OneShotTimer> mIdleTimer;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0e1b775..6108d92 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,6 +25,7 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <ftl/small_map.h>
#include <gui/WindowInfo.h>
@@ -94,36 +95,24 @@
}
}
-void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
- // The current RefreshRateSelector instance may outlive this call, so unbind its idle timer.
- {
- // mRefreshRateSelectorLock is not locked here to avoid the deadlock
- // as the callback can attempt to acquire the lock before stopIdleTimer can finish
- // the execution. It's safe to FakeGuard as main thread is the only thread that
- // writes to the mRefreshRateSelector.
- ftl::FakeGuard guard(mRefreshRateSelectorLock);
- if (mRefreshRateSelector) {
- mRefreshRateSelector->stopIdleTimer();
- mRefreshRateSelector->clearIdleTimerCallbacks();
- }
+void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr newSelectorPtr) {
+ // No need to lock for reads on kMainThreadContext.
+ if (const auto& selectorPtr = FTL_FAKE_GUARD(mRefreshRateSelectorLock, mRefreshRateSelector)) {
+ unbindIdleTimer(*selectorPtr);
}
+
{
- // Clear state that depends on the current instance.
+ // Clear state that depends on the current RefreshRateSelector.
std::scoped_lock lock(mPolicyLock);
mPolicy = {};
}
std::scoped_lock lock(mRefreshRateSelectorLock);
- mRefreshRateSelector = std::move(selectorPtr);
- if (!mRefreshRateSelector) return;
+ mRefreshRateSelector = std::move(newSelectorPtr);
- mRefreshRateSelector->setIdleTimerCallbacks(
- {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
- .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
-
- mRefreshRateSelector->startIdleTimer();
+ if (mRefreshRateSelector) {
+ bindIdleTimer(*mRefreshRateSelector);
+ }
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
@@ -546,6 +535,21 @@
mLayerHistory.clear();
}
+void Scheduler::bindIdleTimer(RefreshRateSelector& selector) {
+ selector.setIdleTimerCallbacks(
+ {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+ .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+ .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+ .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+ selector.startIdleTimer();
+}
+
+void Scheduler::unbindIdleTimer(RefreshRateSelector& selector) {
+ selector.stopIdleTimer();
+ selector.clearIdleTimerCallbacks();
+}
+
void Scheduler::kernelIdleTimerCallback(TimerState state) {
ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
@@ -596,22 +600,36 @@
ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
-void Scheduler::dump(std::string& result) const {
- using base::StringAppendF;
-
- StringAppendF(&result, "+ Touch timer: %s\n",
- mTouchTimer ? mTouchTimer->dump().c_str() : "off");
- StringAppendF(&result, "+ Content detection: %s %s\n\n",
- mFeatures.test(Feature::kContentDetection) ? "on" : "off",
- mLayerHistory.dump().c_str());
-
- mFrameRateOverrideMappings.dump(result);
+void Scheduler::dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
{
+ utils::Dumper::Section section(dumper, "Features"sv);
+
+ for (Feature feature : ftl::enum_range<Feature>()) {
+ if (const auto flagOpt = ftl::flag_name(feature)) {
+ dumper.dump(flagOpt->substr(1), mFeatures.test(feature));
+ }
+ }
+ }
+ {
+ utils::Dumper::Section section(dumper, "Policy"sv);
+
+ dumper.dump("layerHistory"sv, mLayerHistory.dump());
+ dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
+ dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval));
+ }
+
+ mFrameRateOverrideMappings.dump(dumper);
+ dumper.eol();
+
+ {
+ utils::Dumper::Section section(dumper, "Hardware VSYNC"sv);
+
std::lock_guard lock(mHWVsyncLock);
- StringAppendF(&result,
- "mScreenAcquired=%d mPrimaryHWVsyncEnabled=%d mHWVsyncAvailable=%d\n",
- mScreenAcquired.load(), mPrimaryHWVsyncEnabled, mHWVsyncAvailable);
+ dumper.dump("screenAcquired"sv, mScreenAcquired.load());
+ dumper.dump("hwVsyncAvailable"sv, mHWVsyncAvailable);
+ dumper.dump("hwVsyncEnabled"sv, mPrimaryHWVsyncEnabled);
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 901cf74..04f3b69 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -45,6 +45,7 @@
#include "MessageQueue.h"
#include "OneShotTimer.h"
#include "RefreshRateSelector.h"
+#include "Utils/Dumper.h"
#include "VsyncSchedule.h"
namespace android::scheduler {
@@ -108,7 +109,8 @@
void startTimers();
using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
- void setRefreshRateSelector(RefreshRateSelectorPtr) EXCLUDES(mRefreshRateSelectorLock);
+ void setRefreshRateSelector(RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
+ EXCLUDES(mRefreshRateSelectorLock);
void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr);
void unregisterDisplay(PhysicalDisplayId);
@@ -196,7 +198,7 @@
// for a given uid
bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const;
- void dump(std::string&) const;
+ void dump(utils::Dumper&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
@@ -253,6 +255,13 @@
sp<EventThreadConnection> createConnectionInternal(
EventThread*, EventRegistrationFlags eventRegistration = {});
+ void bindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext, mRefreshRateSelectorLock);
+
+ // Blocks until the timer thread exits. `mRefreshRateSelectorLock` must not be locked by the
+ // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+ static void unbindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext)
+ EXCLUDES(mRefreshRateSelectorLock);
+
// Update feature state machine to given state when corresponding timer resets or expires.
void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateSelectorLock);
void idleTimerCallback(TimerState);
@@ -327,9 +336,9 @@
LayerHistory mLayerHistory;
// Timer used to monitor touch events.
- std::optional<OneShotTimer> mTouchTimer;
+ ftl::Optional<OneShotTimer> mTouchTimer;
// Timer used to monitor display power mode.
- std::optional<OneShotTimer> mDisplayPowerTimer;
+ ftl::Optional<OneShotTimer> mDisplayPowerTimer;
ISchedulerCallback& mSchedulerCallback;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
index 2ca55d4..bd4e3c2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Time.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
@@ -17,7 +17,9 @@
#pragma once
#include <chrono>
+#include <string>
+#include <android-base/stringprintf.h>
#include <utils/Timers.h>
namespace android {
@@ -79,4 +81,8 @@
return std::chrono::duration_cast<D>(d).count();
}
+inline std::string to_string(Duration d) {
+ return base::StringPrintf("%.3f ms", ticks<std::milli, float>(d));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 56fa96b..d68b00a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -56,6 +56,7 @@
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <ftl/algorithm.h>
+#include <ftl/concat.h>
#include <ftl/fake_guard.h>
#include <ftl/future.h>
#include <ftl/unit.h>
@@ -163,6 +164,7 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
+using namespace std::string_view_literals;
using namespace hardware::configstore;
using namespace hardware::configstore::V1_0;
@@ -2955,6 +2957,8 @@
// Display modes are reloaded on hotplug reconnect.
if (display->isPrimary()) {
+ // TODO(b/241285876): Annotate `processDisplayAdded` instead.
+ ftl::FakeGuard guard(kMainThreadContext);
mScheduler->setRefreshRateSelector(selectorPtr);
}
@@ -3276,7 +3280,7 @@
ALOGE_IF(error != NO_ERROR,
"Error setting display brightness for display %s: %d (%s)",
- display->getDebugName().c_str(), error, strerror(error));
+ to_string(display->getId()).c_str(), error, strerror(error));
}
display->persistBrightness(needsComposite);
}
@@ -3420,19 +3424,18 @@
features |= Feature::kPresentFences;
}
+ auto selectorPtr = display->holdRefreshRateSelector();
+ if (selectorPtr->kernelIdleTimerController()) {
+ features |= Feature::kKernelIdleTimer;
+ }
+
mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
static_cast<ISchedulerCallback&>(*this),
features);
- {
- auto selectorPtr = display->holdRefreshRateSelector();
- if (selectorPtr->kernelIdleTimerController()) {
- features |= Feature::kKernelIdleTimer;
- }
+ mScheduler->createVsyncSchedule(features);
+ mScheduler->setRefreshRateSelector(selectorPtr);
+ mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr));
- mScheduler->createVsyncSchedule(features);
- mScheduler->setRefreshRateSelector(selectorPtr);
- mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr));
- }
setVsyncEnabled(false);
mScheduler->startTimers();
@@ -4774,17 +4777,18 @@
{"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)},
{"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
{"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)},
- {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })},
{"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
+ {"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
+ {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
+ {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
{"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
{"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
+ {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)},
{"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
- {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
+ {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)},
{"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
- {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
- {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
};
const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
@@ -4908,24 +4912,39 @@
result.append("]");
}
-void SurfaceFlinger::dumpVSync(std::string& result) const {
- mScheduler->dump(result);
+void SurfaceFlinger::dumpScheduler(std::string& result) const {
+ utils::Dumper dumper{result};
+
+ mScheduler->dump(dumper);
+
+ // TODO(b/241286146): Move to Scheduler.
+ {
+ utils::Dumper::Indent indent(dumper);
+ dumper.dump("lastHwcVsyncState"sv, mLastHWCVsyncState);
+ dumper.dump("pendingHwcVsyncState"sv, mHWCVsyncPendingState);
+ }
+ dumper.eol();
+
+ // TODO(b/241285876): Move to DisplayModeController.
+ dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor);
+ dumper.eol();
mRefreshRateStats->dump(result);
- result.append("\n");
+ dumper.eol();
mVsyncConfiguration->dump(result);
StringAppendF(&result,
- " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
+ " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64
+ " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
+}
- StringAppendF(&result, "(mode override by backdoor: %s)\n\n",
- mDebugDisplayModeSetByBackdoor ? "yes" : "no");
-
+void SurfaceFlinger::dumpEvents(std::string& result) const {
mScheduler->dump(mAppConnectionHandle, result);
+}
+
+void SurfaceFlinger::dumpVsync(std::string& result) const {
mScheduler->dumpVsync(result);
- StringAppendF(&result, "mHWCVsyncPendingState=%s mLastHWCVsyncState=%s\n",
- to_string(mHWCVsyncPendingState).c_str(), to_string(mLastHWCVsyncState).c_str());
}
void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const {
@@ -4946,19 +4965,21 @@
utils::Dumper dumper{result};
for (const auto& [id, display] : mPhysicalDisplays) {
+ utils::Dumper::Section section(dumper, ftl::Concat("Display ", id.value).str());
+
+ display.snapshot().dump(dumper);
+
if (const auto device = getDisplayDeviceLocked(id)) {
device->dump(dumper);
}
-
- utils::Dumper::Indent indent(dumper);
- display.snapshot().dump(dumper);
- dumper.eol();
}
for (const auto& [token, display] : mDisplays) {
if (display->isVirtual()) {
+ const auto displayId = display->getId();
+ utils::Dumper::Section section(dumper,
+ ftl::Concat("Virtual Display ", displayId.value).str());
display->dump(dumper);
- dumper.eol();
}
}
}
@@ -5129,7 +5150,7 @@
Layer::miniDumpHeader(result);
const DisplayDevice& ref = *display;
- mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
+ mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
result.append("\n");
}
}
@@ -5169,7 +5190,9 @@
colorizer.bold(result);
result.append("Scheduler:\n");
colorizer.reset(result);
- dumpVSync(result);
+ dumpScheduler(result);
+ dumpEvents(result);
+ dumpVsync(result);
result.append("\n");
StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ee2810d..fd96fbb 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -685,7 +685,7 @@
void commitInputWindowCommands() REQUIRES(mStateLock);
void updateCursorAsync();
- void initScheduler(const sp<const DisplayDevice>&) REQUIRES(mStateLock);
+ void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
@@ -1013,7 +1013,9 @@
void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext);
- void dumpVSync(std::string& result) const REQUIRES(mStateLock);
+ void dumpScheduler(std::string& result) const REQUIRES(mStateLock);
+ void dumpEvents(std::string& result) const REQUIRES(mStateLock);
+ void dumpVsync(std::string& result) const REQUIRES(mStateLock);
void dumpCompositionDisplays(std::string& result) const REQUIRES(mStateLock);
void dumpDisplays(std::string& result) const REQUIRES(mStateLock);
diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h
index 3761f9e..ee94217 100644
--- a/services/surfaceflinger/Utils/Dumper.h
+++ b/services/surfaceflinger/Utils/Dumper.h
@@ -16,10 +16,11 @@
#pragma once
-#include <optional>
#include <string>
#include <string_view>
+#include <ftl/optional.h>
+
namespace android::utils {
// Dumps variables by appending their name and value to the output string. A variable is formatted
@@ -44,16 +45,48 @@
eol();
}
+ void dump(std::string_view name, const std::string& value) {
+ dump(name, static_cast<const std::string_view&>(value));
+ }
+
void dump(std::string_view name, bool value) {
using namespace std::string_view_literals;
dump(name, value ? "true"sv : "false"sv);
}
template <typename T>
- void dump(std::string_view name, const std::optional<T>& value) {
- using namespace std::string_view_literals;
+ void dump(std::string_view name, const std::optional<T>& opt) {
+ if (opt) {
+ dump(name, *opt);
+ } else {
+ using namespace std::string_view_literals;
+ dump(name, "nullopt"sv);
+ }
+ }
+
+ template <typename T>
+ void dump(std::string_view name, const ftl::Optional<T>& opt) {
+ dump(name, static_cast<const std::optional<T>&>(opt));
+ }
+
+ template <typename T, typename... Ts>
+ void dump(std::string_view name, const T& value, const Ts&... rest) {
+ std::string string;
+
+ constexpr bool kIsTuple = sizeof...(Ts) > 0;
+ if constexpr (kIsTuple) {
+ string += '{';
+ }
+
using std::to_string;
- dump(name, value ? to_string(*value) : "nullopt"sv);
+ string += to_string(value);
+
+ if constexpr (kIsTuple) {
+ string += ((", " + to_string(rest)) + ...);
+ string += '}';
+ }
+
+ dump(name, string);
}
struct Indent {
@@ -63,6 +96,21 @@
Dumper& dumper;
};
+ struct Section {
+ Section(Dumper& dumper, std::string_view heading) : dumper(dumper) {
+ dumper.dump({}, heading);
+ indent.emplace(dumper);
+ }
+
+ ~Section() {
+ indent.reset();
+ dumper.eol();
+ }
+
+ Dumper& dumper;
+ std::optional<Indent> indent;
+ };
+
private:
std::string& mOut;
int mIndent = 0;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index f2d008d..bd11a37 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -249,7 +249,9 @@
scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
- dump<scheduler::TestableScheduler>(scheduler, &mFdp);
+ std::string result = mFdp.ConsumeRandomLengthString(kRandomStringLength);
+ utils::Dumper dumper(result);
+ scheduler->dump(dumper);
}
void SchedulerFuzzer::fuzzVSyncReactor() {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 95c9915..ba214d5 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -17,6 +17,7 @@
#pragma once
#include <Scheduler/Scheduler.h>
+#include <ftl/fake_guard.h>
#include <gmock/gmock.h>
#include <gui/ISurfaceComposer.h>
@@ -69,6 +70,11 @@
auto refreshRateSelector() { return holdRefreshRateSelector(); }
bool hasRefreshRateSelectors() const { return !mRefreshRateSelectors.empty(); }
+ void setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ return Scheduler::setRefreshRateSelector(std::move(selectorPtr));
+ }
+
auto& mutableLayerHistory() { return mLayerHistory; }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {