diff options
50 files changed, 1256 insertions, 317 deletions
diff --git a/include/ftl/flags.h b/include/ftl/flags.h index 70aaa0e6dd..cdb4e840a4 100644 --- a/include/ftl/flags.h +++ b/include/ftl/flags.h @@ -125,7 +125,7 @@ public: /* Tests whether all of the given flags are set */ bool all(Flags<F> f) const { return (mFlags & f.mFlags) == f.mFlags; } - Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); } + constexpr Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); } Flags<F>& operator|=(Flags<F> rhs) { mFlags = mFlags | rhs.mFlags; return *this; @@ -217,7 +217,7 @@ inline Flags<F> operator~(F f) { } template <typename F, typename = std::enable_if_t<is_scoped_enum_v<F>>> -Flags<F> operator|(F lhs, F rhs) { +constexpr Flags<F> operator|(F lhs, F rhs) { return static_cast<F>(to_underlying(lhs) | to_underlying(rhs)); } diff --git a/include/ftl/shared_mutex.h b/include/ftl/shared_mutex.h new file mode 100644 index 0000000000..146f5ba4a9 --- /dev/null +++ b/include/ftl/shared_mutex.h @@ -0,0 +1,47 @@ +/* + * Copyright 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 <shared_mutex> + +namespace android::ftl { + +// Wrapper around std::shared_mutex to provide capabilities for thread-safety +// annotations. +// TODO(b/257958323): This class is no longer needed once b/135688034 is fixed (currently blocked on +// b/175635923). +class [[clang::capability("shared_mutex")]] SharedMutex final { + public: + [[clang::acquire_capability()]] void lock() { + mutex_.lock(); + } + [[clang::release_capability()]] void unlock() { + mutex_.unlock(); + } + + [[clang::acquire_shared_capability()]] void lock_shared() { + mutex_.lock_shared(); + } + [[clang::release_shared_capability()]] void unlock_shared() { + mutex_.unlock_shared(); + } + + private: + std::shared_mutex mutex_; +}; + +} // namespace android::ftl diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index fdf41674d4..f17bb7da14 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -495,6 +495,7 @@ cc_library { "libbase", "libbinder", "libbinder_ndk", + "libcutils_sockets", "liblog", "libutils", ], diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 83d0de7835..399667d02b 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -564,6 +564,29 @@ status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { return OK; } +status_t RpcServer::setupRawSocketServer(base::unique_fd socket_fd) { + RpcTransportFd transportFd(std::move(socket_fd)); + if (!transportFd.fd.ok()) { + int savedErrno = errno; + ALOGE("Could not get initialized Unix socket: %s", strerror(savedErrno)); + return -savedErrno; + } + // Right now, we create all threads at once, making accept4 slow. To avoid hanging the client, + // the backlog is increased to a large number. + // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced + // to 1. + if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) { + int savedErrno = errno; + ALOGE("Could not listen initialized Unix socket: %s", strerror(savedErrno)); + return -savedErrno; + } + if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) { + ALOGE("Another thread has set up server while calling setupSocketServer. Race?"); + return status; + } + return OK; +} + void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) { const std::vector<uint8_t>& id = session->mId; LOG_ALWAYS_FATAL_IF(id.empty(), "Server sessions must be initialized with ID"); diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 81ae26a344..4ad0a47920 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -71,6 +71,16 @@ public: [[nodiscard]] status_t setupUnixDomainServer(const char* path); /** + * Sets up an RPC server with a raw socket file descriptor. + * The socket should be created and bound to a socket address already, e.g. + * the socket can be created in init.rc. + * + * This method is used in the libbinder_rpc_unstable API + * RunInitUnixDomainRpcServer(). + */ + [[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd); + + /** * Creates an RPC server at the current port. */ [[nodiscard]] status_t setupVsockServer(unsigned int port); diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index e4a9f9975a..dd177aff7f 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -42,6 +42,20 @@ bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* c AIBinder* VsockRpcClient(unsigned int cid, unsigned int port); +// Starts a Unix domain RPC server with a given init-managed Unix domain `name` and +// a given root IBinder object. +// The socket should be created in init.rc with the same `name`. +// +// This function sets up the server, calls readyCallback with a given param, and +// then joins before returning. +bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name, + void (*readyCallback)(void* param), void* param); + +// Gets the service via the RPC binder with Unix domain socket with the given +// Unix socket `name`. +// The final Unix domain socket path name is /dev/socket/`name`. +AIBinder* UnixDomainRpcClient(const char* name); + // Connect to an RPC server with preconnected file descriptors. // // requestFd should connect to the server and return a valid file descriptor, or diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index 1f38bb925c..ae07aeecd1 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -19,6 +19,7 @@ #include <android/binder_libbinder.h> #include <binder/RpcServer.h> #include <binder/RpcSession.h> +#include <cutils/sockets.h> #include <linux/vm_sockets.h> using android::OK; @@ -30,6 +31,17 @@ using android::base::unique_fd; extern "C" { +void RunRpcServer(android::sp<RpcServer>& server, AIBinder* service, + void (*readyCallback)(void* param), void* param) { + server->setRootObject(AIBinder_toPlatformBinder(service)); + + if (readyCallback) readyCallback(param); + server->join(); + + // Shutdown any open sessions since server failed. + (void)server->shutdown(); +} + bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context), void* factoryContext, unsigned int port) { auto server = RpcServer::make(); @@ -60,13 +72,7 @@ bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port, << " error: " << statusToString(status).c_str(); return false; } - server->setRootObject(AIBinder_toPlatformBinder(service)); - - if (readyCallback) readyCallback(param); - server->join(); - - // Shutdown any open sessions since server failed. - (void)server->shutdown(); + RunRpcServer(server, service, readyCallback, param); return true; } @@ -84,6 +90,31 @@ AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) { return AIBinder_fromPlatformBinder(session->getRootObject()); } +bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name, + void (*readyCallback)(void* param), void* param) { + auto server = RpcServer::make(); + auto fd = unique_fd(android_get_control_socket(name)); + if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) { + LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name + << " error: " << statusToString(status).c_str(); + return false; + } + RunRpcServer(server, service, readyCallback, param); + return true; +} + +AIBinder* UnixDomainRpcClient(const char* name) { + std::string pathname(name); + pathname = ANDROID_SOCKET_DIR "/" + pathname; + auto session = RpcSession::make(); + if (status_t status = session->setupUnixDomainClient(pathname.c_str()); status != OK) { + LOG(ERROR) << "Failed to set up Unix Domain RPC client with path: " << pathname + << " error: " << statusToString(status).c_str(); + return nullptr; + } + return AIBinder_fromPlatformBinder(session->getRootObject()); +} + AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) { auto session = RpcSession::make(); auto request = [=] { return unique_fd{requestFd(param)}; }; diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt index 347831a5fc..f9c7bcf117 100644 --- a/libs/binder/libbinder_rpc_unstable.map.txt +++ b/libs/binder/libbinder_rpc_unstable.map.txt @@ -3,6 +3,8 @@ LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only RunVsockRpcServer; RunVsockRpcServerCallback; VsockRpcClient; + RunInitUnixDomainRpcServer; + UnixDomainRpcClient; RpcPreconnectedClient; local: *; diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 5ebc27f694..9771cc9a89 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -20,6 +20,7 @@ rust_library { "libbinder_rs", "libdowncast_rs", "liblibc", + "liblog_rust", ], apex_available: [ "com.android.compos", diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs index 4343ff4f83..48c787b2fa 100644 --- a/libs/binder/rust/rpcbinder/src/client.rs +++ b/libs/binder/rust/rpcbinder/src/client.rs @@ -15,6 +15,7 @@ */ use binder::{unstable_api::new_spibinder, FromIBinder, SpIBinder, StatusCode, Strong}; +use std::ffi::CString; use std::os::{ raw::{c_int, c_void}, unix::io::RawFd, @@ -35,6 +36,27 @@ pub fn get_vsock_rpc_interface<T: FromIBinder + ?Sized>( interface_cast(get_vsock_rpc_service(cid, port)) } +/// Connects to an RPC Binder server over Unix domain socket. +pub fn get_unix_domain_rpc_service(socket_name: &str) -> Option<SpIBinder> { + let socket_name = match CString::new(socket_name) { + Ok(s) => s, + Err(e) => { + log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); + return None; + } + }; + // SAFETY: AIBinder returned by UnixDomainRpcClient has correct reference count, + // and the ownership can safely be taken by new_spibinder. + unsafe { new_spibinder(binder_rpc_unstable_bindgen::UnixDomainRpcClient(socket_name.as_ptr())) } +} + +/// Connects to an RPC Binder server for a particular interface over Unix domain socket. +pub fn get_unix_domain_rpc_interface<T: FromIBinder + ?Sized>( + socket_name: &str, +) -> Result<Strong<T>, StatusCode> { + interface_cast(get_unix_domain_rpc_service(socket_name)) +} + /// Connects to an RPC Binder server, using the given callback to get (and take ownership of) /// file descriptors already connected to it. pub fn get_preconnected_rpc_service( diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index fb6b90c8e1..89a49a468c 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -20,7 +20,9 @@ mod client; mod server; pub use client::{ - get_preconnected_rpc_interface, get_preconnected_rpc_service, get_vsock_rpc_interface, - get_vsock_rpc_service, + get_preconnected_rpc_interface, get_preconnected_rpc_service, get_unix_domain_rpc_interface, + get_unix_domain_rpc_service, get_vsock_rpc_interface, get_vsock_rpc_service, +}; +pub use server::{ + run_init_unix_domain_rpc_server, run_vsock_rpc_server, run_vsock_rpc_server_with_factory, }; -pub use server::{run_vsock_rpc_server, run_vsock_rpc_server_with_factory}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs index 80092977fa..b350a133a8 100644 --- a/libs/binder/rust/rpcbinder/src/server.rs +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -18,7 +18,7 @@ use binder::{ unstable_api::{AIBinder, AsNative}, SpIBinder, }; -use std::{os::raw, ptr::null_mut}; +use std::{ffi::CString, os::raw, ptr::null_mut}; /// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock /// port. @@ -35,7 +35,28 @@ where F: FnOnce(), { let mut ready_notifier = ReadyNotifier(Some(on_ready)); - ready_notifier.run_server(service, port) + ready_notifier.run_vsock_server(service, port) +} + +/// Runs a binder RPC server, serving the supplied binder service implementation on the given +/// socket file name. The socket should be initialized in init.rc with the same name. +/// +/// If and when the server is ready for connections, `on_ready` is called to allow appropriate +/// action to be taken - e.g. to notify clients that they may now attempt to connect. +/// +/// The current thread is joined to the binder thread pool to handle incoming messages. +/// +/// Returns true if the server has shutdown normally, false if it failed in some way. +pub fn run_init_unix_domain_rpc_server<F>( + service: SpIBinder, + socket_name: &str, + on_ready: F, +) -> bool +where + F: FnOnce(), +{ + let mut ready_notifier = ReadyNotifier(Some(on_ready)); + ready_notifier.run_init_unix_domain_server(service, socket_name) } struct ReadyNotifier<F>(Option<F>) @@ -46,7 +67,7 @@ impl<F> ReadyNotifier<F> where F: FnOnce(), { - fn run_server(&mut self, mut service: SpIBinder, port: u32) -> bool { + fn run_vsock_server(&mut self, mut service: SpIBinder, port: u32) -> bool { let service = service.as_native_mut(); let param = self.as_void_ptr(); @@ -64,6 +85,31 @@ where } } + fn run_init_unix_domain_server(&mut self, mut service: SpIBinder, socket_name: &str) -> bool { + let socket_name = match CString::new(socket_name) { + Ok(s) => s, + Err(e) => { + log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); + return false; + } + }; + let service = service.as_native_mut(); + let param = self.as_void_ptr(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + // RunInitUnixDomainRpcServer does not retain a reference to `ready_callback` or `param`; + // it only uses them before it returns, which is during the lifetime of `self`. + unsafe { + binder_rpc_unstable_bindgen::RunInitUnixDomainRpcServer( + service, + socket_name.as_ptr(), + Some(Self::ready_callback), + param, + ) + } + } + fn as_void_ptr(&mut self) -> *mut raw::c_void { self as *mut _ as *mut raw::c_void } diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl index 4cdeac4a97..aac4b04dae 100644 --- a/libs/binder/tests/BinderRpcTestServerConfig.aidl +++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl @@ -22,5 +22,6 @@ parcelable BinderRpcTestServerConfig { int serverVersion; int vsockPort; int unixBootstrapFd; // Inherited from parent + int socketFd; @utf8InCpp String addr; } diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 004dea317d..79bd9d4993 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -129,6 +129,15 @@ static unsigned int allocateVsockPort() { return vsockPort++; } +static base::unique_fd initUnixSocket(std::string addr) { + auto socket_addr = UnixSocketAddress(addr.c_str()); + base::unique_fd fd( + TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX))); + CHECK(fd.ok()); + CHECK_EQ(0, TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize()))); + return fd; +} + // Destructors need to be defined, even if pure virtual ProcessSession::~ProcessSession() {} @@ -243,13 +252,18 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( singleThreaded ? "_single_threaded" : "", noKernel ? "_no_kernel" : ""); - base::unique_fd bootstrapClientFd, bootstrapServerFd; + base::unique_fd bootstrapClientFd, bootstrapServerFd, socketFd; // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec. // This is because we cannot pass ParcelFileDescriptor over a pipe. if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &bootstrapServerFd)) { int savedErrno = errno; LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno); } + auto addr = allocateSocketAddress(); + // Initializes the socket before the fork/exec. + if (socketType == SocketType::UNIX_RAW) { + socketFd = initUnixSocket(addr); + } auto ret = std::make_unique<LinuxProcessSession>( Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) { @@ -265,8 +279,9 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity); serverConfig.serverVersion = serverVersion; serverConfig.vsockPort = allocateVsockPort(); - serverConfig.addr = allocateSocketAddress(); + serverConfig.addr = addr; serverConfig.unixBootstrapFd = bootstrapServerFd.get(); + serverConfig.socketFd = socketFd.get(); for (auto mode : options.serverSupportedFileDescriptorTransportModes) { serverConfig.serverSupportedFileDescriptorTransportModes.push_back( static_cast<int32_t>(mode)); @@ -312,6 +327,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( return connectTo(UnixSocketAddress(serverConfig.addr.c_str())); }); break; + case SocketType::UNIX_RAW: case SocketType::UNIX: status = session->setupUnixDomainClient(serverConfig.addr.c_str()); break; @@ -1042,7 +1058,8 @@ static bool testSupportVsockLoopback() { } static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { - std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET}; + std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET, + SocketType::UNIX_RAW}; if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED); @@ -1284,6 +1301,17 @@ public: mAcceptConnection = &Server::recvmsgServerConnection; mConnectToServer = [this] { return connectToUnixBootstrap(mBootstrapSocket); }; } break; + case SocketType::UNIX_RAW: { + auto addr = allocateSocketAddress(); + auto status = rpcServer->setupRawSocketServer(initUnixSocket(addr)); + if (status != OK) { + return AssertionFailure() + << "setupRawSocketServer: " << statusToString(status); + } + mConnectToServer = [addr] { + return connectTo(UnixSocketAddress(addr.c_str())); + }; + } break; case SocketType::VSOCK: { auto port = allocateVsockPort(); auto status = rpcServer->setupVsockServer(port); diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index dc7d26491b..654e16c3be 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -69,6 +69,7 @@ enum class SocketType { PRECONNECTED, UNIX, UNIX_BOOTSTRAP, + UNIX_RAW, VSOCK, INET, }; @@ -81,6 +82,8 @@ static inline std::string PrintToString(SocketType socketType) { return "unix_domain_socket"; case SocketType::UNIX_BOOTSTRAP: return "unix_domain_socket_bootstrap"; + case SocketType::UNIX_RAW: + return "raw_uds"; case SocketType::VSOCK: return "vm_socket"; case SocketType::INET: diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h index 721fbfe1e1..5a78782b9b 100644 --- a/libs/binder/tests/binderRpcTestFixture.h +++ b/libs/binder/tests/binderRpcTestFixture.h @@ -108,7 +108,8 @@ public: bool supportsFdTransport() const { return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS && (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX || - socketType() == SocketType::UNIX_BOOTSTRAP); + socketType() == SocketType::UNIX_BOOTSTRAP || + socketType() == SocketType::UNIX_RAW); } void SetUp() override { diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp index a922b21dd7..cc40995a52 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -54,6 +54,9 @@ int main(int argc, const char* argv[]) { case SocketType::UNIX_BOOTSTRAP: CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(unixBootstrapFd))); break; + case SocketType::UNIX_RAW: + CHECK_EQ(OK, server->setupRawSocketServer(base::unique_fd(serverConfig.socketFd))); + break; case SocketType::VSOCK: CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort)); break; diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp index 1e8d93d81f..f960442859 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -86,7 +86,7 @@ TEST_P(BinderRpc, SeparateRootObject) { SocketType type = std::get<0>(GetParam()); if (type == SocketType::PRECONNECTED || type == SocketType::UNIX || - type == SocketType::UNIX_BOOTSTRAP) { + type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) { // we can't get port numbers for unix sockets return; } diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 81113bc211..df0b271a9b 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -24,6 +24,7 @@ cc_test { "match_test.cpp", "non_null_test.cpp", "optional_test.cpp", + "shared_mutex_test.cpp", "small_map_test.cpp", "small_vector_test.cpp", "static_vector_test.cpp", diff --git a/libs/ftl/shared_mutex_test.cpp b/libs/ftl/shared_mutex_test.cpp new file mode 100644 index 0000000000..6da7061ae0 --- /dev/null +++ b/libs/ftl/shared_mutex_test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 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 <ftl/shared_mutex.h> +#include <gtest/gtest.h> +#include <ftl/fake_guard.h> + +namespace android::test { + +TEST(SharedMutex, SharedLock) { + ftl::SharedMutex mutex; + std::shared_lock shared_lock(mutex); + + { std::shared_lock shared_lock2(mutex); } +} + +TEST(SharedMutex, ExclusiveLock) { + ftl::SharedMutex mutex; + std::unique_lock unique_lock(mutex); +} + +TEST(SharedMutex, Annotations) { + struct { + void foo() FTL_ATTRIBUTE(requires_shared_capability(mutex)) { num++; } + void bar() FTL_ATTRIBUTE(requires_capability(mutex)) { num++; } + void baz() { + std::shared_lock shared_lock(mutex); + num++; + } + ftl::SharedMutex mutex; + int num = 0; + + } s; + + { + // TODO(b/257958323): Use an RAII class instead of locking manually. + s.mutex.lock_shared(); + s.foo(); + s.baz(); + s.mutex.unlock_shared(); + } + s.mutex.lock(); + s.bar(); + s.mutex.unlock(); +} + +} // namespace android::test diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp index 52d9540eeb..bd640df81e 100644 --- a/libs/gui/DisplayInfo.cpp +++ b/libs/gui/DisplayInfo.cpp @@ -20,8 +20,13 @@ #include <gui/DisplayInfo.h> #include <private/gui/ParcelUtils.h> +#include <android-base/stringprintf.h> #include <log/log.h> +#include <inttypes.h> + +#define INDENT " " + namespace android::gui { // --- DisplayInfo --- @@ -67,4 +72,17 @@ status_t DisplayInfo::writeToParcel(android::Parcel* parcel) const { return OK; } +void DisplayInfo::dump(std::string& out, const char* prefix) const { + using android::base::StringAppendF; + + out += prefix; + StringAppendF(&out, "DisplayViewport[id=%" PRId32 "]\n", displayId); + out += prefix; + StringAppendF(&out, INDENT "Width=%" PRId32 ", Height=%" PRId32 "\n", logicalWidth, + logicalHeight); + std::string transformPrefix(prefix); + transformPrefix.append(INDENT); + transform.dump(out, "Transform", transformPrefix.c_str()); +} + } // namespace android::gui diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h index 74f33a2a87..42b62c755c 100644 --- a/libs/gui/include/gui/DisplayInfo.h +++ b/libs/gui/include/gui/DisplayInfo.h @@ -41,6 +41,8 @@ struct DisplayInfo : public Parcelable { status_t writeToParcel(android::Parcel*) const override; status_t readFromParcel(const android::Parcel*) override; + + void dump(std::string& result, const char* prefix = "") const; }; } // namespace android::gui
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 99c4936f32..ab5c5ef99c 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -46,6 +46,7 @@ filegroup { "LatencyAggregator.cpp", "LatencyTracker.cpp", "Monitor.cpp", + "TouchedWindow.cpp", "TouchState.cpp", ], } diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 33e7e17ec0..ec9701ac24 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -308,7 +308,8 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; -DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, +DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform, const ui::Transform& rawTransform, float globalScaleFactor) : seq(nextSeq()), diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 60f319a056..f8019126f3 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -223,7 +223,7 @@ struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 std::shared_ptr<EventEntry> eventEntry; // the event to dispatch - int32_t targetFlags; + ftl::Flags<InputTarget::Flags> targetFlags; ui::Transform transform; ui::Transform rawTransform; float globalScaleFactor; @@ -238,13 +238,15 @@ struct DispatchEntry { int32_t resolvedAction; int32_t resolvedFlags; - DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - const ui::Transform& transform, const ui::Transform& rawTransform, - float globalScaleFactor); + DispatchEntry(std::shared_ptr<EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform, + const ui::Transform& rawTransform, float globalScaleFactor); - inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } + inline bool hasForegroundTarget() const { + return targetFlags.test(InputTarget::Flags::FOREGROUND); + } - inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; } + inline bool isSplit() const { return targetFlags.test(InputTarget::Flags::SPLIT); } private: static volatile int32_t sNextSeqAtomic; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f6982173af..7b7c42a211 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -29,6 +29,7 @@ #include <gui/SurfaceComposerClient.h> #endif #include <input/InputDevice.h> +#include <input/PrintTools.h> #include <powermanager/PowerManager.h> #include <unistd.h> #include <utils/Trace.h> @@ -50,6 +51,7 @@ #define INDENT3 " " #define INDENT4 " " +using namespace android::ftl::flag_operators; using android::base::HwTimeoutMultiplier; using android::base::Result; using android::base::StringPrintf; @@ -241,9 +243,9 @@ std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTi } dump.append(INDENT4); dump += entry.eventEntry->getDescription(); - dump += StringPrintf(", seq=%" PRIu32 - ", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 "ms", - entry.seq, entry.targetFlags, entry.resolvedAction, + dump += StringPrintf(", seq=%" PRIu32 ", targetFlags=%s, resolvedAction=%d, age=%" PRId64 + "ms", + entry.seq, entry.targetFlags.string().c_str(), entry.resolvedAction, ns2ms(currentTime - entry.eventEntry->eventTime)); if (entry.deliveryTime != 0) { // This entry was delivered, so add information on how long we've been waiting @@ -288,9 +290,9 @@ bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) first->applicationInfo.token == second->applicationInfo.token; } -std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, - std::shared_ptr<EventEntry> eventEntry, - int32_t inputTargetFlags) { +std::unique_ptr<DispatchEntry> createDispatchEntry( + const InputTarget& inputTarget, std::shared_ptr<EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> inputTargetFlags) { if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, @@ -483,11 +485,11 @@ bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER); } -// Determines if the given window can be targeted as InputTarget::FLAG_FOREGROUND. +// Determines if the given window can be targeted as InputTarget::Flags::FOREGROUND. // Foreground events are only sent to "foreground targetable" windows, but not all gestures sent to // such window are necessarily targeted with the flag. For example, an event with ACTION_OUTSIDE can // be sent to such a window, but it is not a foreground event and doesn't use -// InputTarget::FLAG_FOREGROUND. +// InputTarget::Flags::FOREGROUND. bool canReceiveForegroundTouches(const WindowInfo& info) { // A non-touchable window can still receive touch events (e.g. in the case of // STYLUS_INTERCEPTOR), so prevent such windows from receiving foreground events for touches. @@ -1100,7 +1102,7 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI if (addOutsideTargets && info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) { - touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, + touchState->addOrUpdateWindow(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, BitSet32(0)); } } @@ -1371,7 +1373,7 @@ void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<F } InputTarget target; target.inputChannel = channel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; entry->dispatchInProgress = true; std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") + channel->getName(); @@ -1445,7 +1447,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( } InputTarget target; target.inputChannel = channel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; entry->dispatchInProgress = true; dispatchEventLocked(currentTime, entry, {target}); @@ -1482,7 +1484,7 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked } InputTarget target; target.inputChannel = channel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; inputTargets.push_back(target); } return inputTargets; @@ -1595,7 +1597,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key std::vector<InputTarget> inputTargets; addWindowTargetLocked(focusedWindow, - InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, + InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS, BitSet32(0), getDownTime(*entry), inputTargets); // Add monitor channels from event's or focused display. @@ -1708,7 +1710,8 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< if (injectionResult == InputEventInjectionResult::SUCCEEDED) { LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr); addWindowTargetLocked(focusedWindow, - InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, + InputTarget::Flags::FOREGROUND | + InputTarget::Flags::DISPATCH_AS_IS, BitSet32(0), getDownTime(*entry), inputTargets); } } @@ -1760,7 +1763,7 @@ void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<Dr } InputTarget target; target.inputChannel = channel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; entry->dispatchInProgress = true; dispatchEventLocked(currentTime, entry, {target}); } @@ -2195,20 +2198,20 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( } // Set target flags. - int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS; + ftl::Flags<InputTarget::Flags> targetFlags = InputTarget::Flags::DISPATCH_AS_IS; if (canReceiveForegroundTouches(*windowHandle->getInfo())) { // There should only be one touched window that can be "foreground" for the pointer. - targetFlags |= InputTarget::FLAG_FOREGROUND; + targetFlags |= InputTarget::Flags::FOREGROUND; } if (isSplit) { - targetFlags |= InputTarget::FLAG_SPLIT; + targetFlags |= InputTarget::Flags::SPLIT; } if (isWindowObscuredAtPointLocked(windowHandle, x, y)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED; } else if (isWindowObscuredLocked(windowHandle)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; } // Update the temporary touch state. @@ -2232,11 +2235,10 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( // If the pointer is not currently down, then ignore the event. if (!tempTouchState.isDown()) { - if (DEBUG_FOCUS) { - ALOGD("Dropping event because the pointer is not down or we previously " - "dropped the pointer down event in display %" PRId32, - displayId); - } + ALOGD_IF(DEBUG_FOCUS, + "Dropping event because the pointer is not down or we previously " + "dropped the pointer down event in display %" PRId32 ": %s", + displayId, entry.getDescription().c_str()); outInjectionResult = InputEventInjectionResult::FAILED; goto Failed; } @@ -2276,7 +2278,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( } // Make a slippery exit from the old window. tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, + InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0)); // Make a slippery entrance into the new window. @@ -2284,17 +2286,18 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( isSplit = !isFromMouse; } - int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; + ftl::Flags<InputTarget::Flags> targetFlags = + InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER; if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) { - targetFlags |= InputTarget::FLAG_FOREGROUND; + targetFlags |= InputTarget::Flags::FOREGROUND; } if (isSplit) { - targetFlags |= InputTarget::FLAG_SPLIT; + targetFlags |= InputTarget::Flags::SPLIT; } if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED; } else if (isWindowObscuredLocked(newTouchedWindowHandle)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; } BitSet32 pointerIds; @@ -2317,7 +2320,8 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( mLastHoverWindowHandle->getName().c_str()); } tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); + InputTarget::Flags::DISPATCH_AS_HOVER_EXIT, + BitSet32(0)); } // Let the new window know that the hover sequence is starting, unless we already did it @@ -2330,7 +2334,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( newHoverWindowHandle->getName().c_str()); } tempTouchState.addOrUpdateWindow(newHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, + InputTarget::Flags::DISPATCH_AS_HOVER_ENTER, BitSet32(0)); } } @@ -2343,7 +2347,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( [](const TouchedWindow& touchedWindow) { return !canReceiveForegroundTouches( *touchedWindow.windowHandle->getInfo()) || - (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0; + touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND); })) { ALOGI("Dropping event because there is no touched window on display %d to receive it: %s", displayId, entry.getDescription().c_str()); @@ -2355,7 +2359,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( if (entry.injectionState != nullptr) { std::string errs; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) { // Allow ACTION_OUTSIDE events generated by targeted injection to be // dispatched to any uid, since the coords will be zeroed out later. continue; @@ -2380,11 +2384,11 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( if (foregroundWindowHandle) { const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) { sp<WindowInfoHandle> windowInfoHandle = touchedWindow.windowHandle; if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) { tempTouchState.addOrUpdateWindow(windowInfoHandle, - InputTarget::FLAG_ZERO_COORDS, + InputTarget::Flags::ZERO_COORDS, BitSet32(0)); } } @@ -2411,13 +2415,12 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( if (info->displayId == displayId && windowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::IS_WALLPAPER)) { - tempTouchState - .addOrUpdateWindow(windowHandle, - InputTarget::FLAG_WINDOW_IS_OBSCURED | - InputTarget:: - FLAG_WINDOW_IS_PARTIALLY_OBSCURED | - InputTarget::FLAG_DISPATCH_AS_IS, - BitSet32(0), entry.eventTime); + tempTouchState.addOrUpdateWindow(windowHandle, + InputTarget::Flags::WINDOW_IS_OBSCURED | + InputTarget::Flags:: + WINDOW_IS_PARTIALLY_OBSCURED | + InputTarget::Flags::DISPATCH_AS_IS, + BitSet32(0), entry.eventTime); } } } @@ -2443,10 +2446,8 @@ Failed: if (isHoverAction) { // Started hovering, therefore no longer down. if (oldState && oldState->isDown()) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Hover received while pointer was " - "down."); - } + ALOGD_IF(DEBUG_FOCUS, + "Conflicting pointer actions: Hover received while pointer was down."); *outConflictingPointerActions = true; } tempTouchState.reset(); @@ -2462,9 +2463,7 @@ Failed: } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (oldState && oldState->isDown()) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Down received while already down."); - } + ALOGD("Conflicting pointer actions: Down received while already down."); *outConflictingPointerActions = true; } } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -2612,7 +2611,8 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { } void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, + ftl::Flags<InputTarget::Flags> targetFlags, + BitSet32 pointerIds, std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const { std::vector<InputTarget>::iterator it = @@ -2660,7 +2660,7 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) { InputTarget target; target.inputChannel = monitor.inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; // target.firstDownTimeInTarget is not set for global monitors. It is only required in split // touch and global monitoring works as intended even without setting firstDownTimeInTarget if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { @@ -2692,7 +2692,7 @@ static bool canBeObscuredBy(const sp<WindowInfoHandle>& windowHandle, // We do want to potentially flag touchable windows even if they have 0 // opacity, since they can consume touches and alter the effects of the // user interaction (eg. apps that rely on - // FLAG_WINDOW_IS_PARTIALLY_OBSCURED should still be told about those + // Flags::WINDOW_IS_PARTIALLY_OBSCURED should still be told about those // windows), hence we also check for FLAG_NOT_TOUCHABLE. return false; } else if (info->ownerUid == otherInfo->ownerUid) { @@ -2921,9 +2921,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ATRACE_NAME(message.c_str()); } if (DEBUG_DISPATCH_CYCLE) { - ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " + ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, " "globalScaleFactor=%f, pointerIds=0x%x %s", - connection->getInputChannelName().c_str(), inputTarget.flags, + connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(), inputTarget.globalScaleFactor, inputTarget.pointerIds.value, inputTarget.getPointerInfoString().c_str()); } @@ -2940,9 +2940,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } // Split a motion event if needed. - if (inputTarget.flags & InputTarget::FLAG_SPLIT) { + if (inputTarget.flags.test(InputTarget::Flags::SPLIT)) { LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION, - "Entry type %s should not have FLAG_SPLIT", + "Entry type %s should not have Flags::SPLIT", ftl::enum_string(eventEntry->type).c_str()); const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry); @@ -2991,17 +2991,17 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, // Enqueue dispatch entries for the requested modes. enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); + InputTarget::Flags::DISPATCH_AS_HOVER_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_OUTSIDE); + InputTarget::Flags::DISPATCH_AS_OUTSIDE); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); + InputTarget::Flags::DISPATCH_AS_HOVER_ENTER); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_IS); + InputTarget::Flags::DISPATCH_AS_IS); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); + InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); + InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER); // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.empty()) { @@ -3012,18 +3012,20 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry> eventEntry, const InputTarget& inputTarget, - int32_t dispatchMode) { + ftl::Flags<InputTarget::Flags> dispatchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", connection->getInputChannelName().c_str(), - dispatchModeToString(dispatchMode).c_str()); + dispatchMode.string().c_str()); ATRACE_NAME(message.c_str()); } - int32_t inputTargetFlags = inputTarget.flags; - if (!(inputTargetFlags & dispatchMode)) { + ftl::Flags<InputTarget::Flags> inputTargetFlags = inputTarget.flags; + if (!inputTargetFlags.any(dispatchMode)) { return; } - inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode; + + inputTargetFlags.clear(InputTarget::DISPATCH_MASK); + inputTargetFlags |= dispatchMode; // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. @@ -3060,15 +3062,15 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio constexpr int32_t DEFAULT_RESOLVED_EVENT_ID = static_cast<int32_t>(IdGenerator::Source::OTHER); dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID; - if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { + } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { + } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_ENTER)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { + } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) { dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; } else { dispatchEntry->resolvedAction = motionEntry.action; @@ -3088,10 +3090,10 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio } dispatchEntry->resolvedFlags = motionEntry.flags; - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { + if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_OBSCURED)) { dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; } - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) { + if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED)) { dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; } @@ -3191,8 +3193,7 @@ void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry, std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens; std::vector<sp<Connection>> newConnections; for (const InputTarget& target : targets) { - if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) == - InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) { continue; // Skip windows that receive ACTION_OUTSIDE } @@ -3251,7 +3252,7 @@ status_t InputDispatcher::publishMotionEvent(Connection& connection, // Set the X and Y offset and X and Y scale depending on the input source. if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) && - !(dispatchEntry.targetFlags & InputTarget::FLAG_ZERO_COORDS)) { + !(dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS))) { float globalScaleFactor = dispatchEntry.globalScaleFactor; if (globalScaleFactor != 1.0f) { for (uint32_t i = 0; i < motionEntry.pointerCount; i++) { @@ -3264,7 +3265,7 @@ status_t InputDispatcher::publishMotionEvent(Connection& connection, } usingCoords = scaledCoords; } - } else if (dispatchEntry.targetFlags & InputTarget::FLAG_ZERO_COORDS) { + } else if (dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS)) { // We don't want the dispatch target to know the coordinates for (uint32_t i = 0; i < motionEntry.pointerCount; i++) { scaledCoords[i].clear(); @@ -3666,7 +3667,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( target.globalScaleFactor = windowInfo->globalScaleFactor; } target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; const bool wasEmpty = connection->outboundQueue.empty(); @@ -3701,7 +3702,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target, - InputTarget::FLAG_DISPATCH_AS_IS); + InputTarget::Flags::DISPATCH_AS_IS); } // If the outbound queue was previously empty, start the dispatch cycle going. @@ -3737,7 +3738,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( target.globalScaleFactor = windowInfo->globalScaleFactor; } target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = InputTarget::Flags::DISPATCH_AS_IS; const bool wasEmpty = connection->outboundQueue.empty(); for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { @@ -3763,7 +3764,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target, - InputTarget::FLAG_DISPATCH_AS_IS); + InputTarget::Flags::DISPATCH_AS_IS); } // If the outbound queue was previously empty, start the dispatch cycle going. @@ -4826,7 +4827,7 @@ void InputDispatcher::setInputWindowsLocked( synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); // Since we are about to drop the touch, cancel the events for the wallpaper as // well. - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND && + if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && touchedWindow.windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow(); @@ -5144,16 +5145,16 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } // Erase old window. - int32_t oldTargetFlags = touchedWindow->targetFlags; + ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags; BitSet32 pointerIds = touchedWindow->pointerIds; state->removeWindowByToken(fromToken); // Add new window. nsecs_t downTimeInTarget = now(); - int32_t newTargetFlags = - oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); + ftl::Flags<InputTarget::Flags> newTargetFlags = + oldTargetFlags & (InputTarget::Flags::SPLIT | InputTarget::Flags::DISPATCH_AS_IS); if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) { - newTargetFlags |= InputTarget::FLAG_FOREGROUND; + newTargetFlags |= InputTarget::Flags::FOREGROUND; } state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds, downTimeInTarget); @@ -5207,7 +5208,7 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindowLocked(int32_t sp<WindowInfoHandle> touchedForegroundWindow; // If multiple foreground windows are touched, return nullptr for (const TouchedWindow& window : state.windows) { - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) { if (touchedForegroundWindow != nullptr) { ALOGI("Two or more foreground windows: %s and %s", touchedForegroundWindow->getName().c_str(), @@ -5320,22 +5321,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!mTouchStatesByDisplay.empty()) { dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); for (const auto& [displayId, state] : mTouchStatesByDisplay) { - dump += StringPrintf(INDENT2 "%d: deviceId=%d, source=0x%08x\n", displayId, - state.deviceId, state.source); - if (!state.windows.empty()) { - dump += INDENT3 "Windows:\n"; - for (size_t i = 0; i < state.windows.size(); i++) { - const TouchedWindow& touchedWindow = state.windows[i]; - dump += StringPrintf(INDENT4 "%zu: name='%s', pointerIds=0x%0x, " - "targetFlags=0x%x, firstDownTimeInTarget=%" PRId64 - "ms\n", - i, touchedWindow.windowHandle->getName().c_str(), - touchedWindow.pointerIds.value, touchedWindow.targetFlags, - ns2ms(touchedWindow.firstDownTimeInTarget.value_or(0))); - } - } else { - dump += INDENT3 "Windows: <none>\n"; - } + std::string touchStateDump = addLinePrefix(state.dump(), INDENT2); + dump += INDENT2 + std::to_string(displayId) + " : " + touchStateDump; } } else { dump += INDENT "TouchStates: <no displays touched>\n"; diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 0ddbbeb27e..5efb39e0f2 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -553,7 +553,7 @@ private: const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock); void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, + ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds, std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const REQUIRES(mLock); void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId) @@ -600,8 +600,8 @@ private: std::shared_ptr<EventEntry>, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>, - const InputTarget& inputTarget, int32_t dispatchMode) - REQUIRES(mLock); + const InputTarget& inputTarget, + ftl::Flags<InputTarget::Flags> dispatchMode) REQUIRES(mLock); status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const; void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index 2df97d9a12..2f39480555 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -24,24 +24,6 @@ using android::base::StringPrintf; namespace android::inputdispatcher { -std::string dispatchModeToString(int32_t dispatchMode) { - switch (dispatchMode) { - case InputTarget::FLAG_DISPATCH_AS_IS: - return "DISPATCH_AS_IS"; - case InputTarget::FLAG_DISPATCH_AS_OUTSIDE: - return "DISPATCH_AS_OUTSIDE"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER: - return "DISPATCH_AS_HOVER_ENTER"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT: - return "DISPATCH_AS_HOVER_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT: - return "DISPATCH_AS_SLIPPERY_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER: - return "DISPATCH_AS_SLIPPERY_ENTER"; - } - return StringPrintf("%" PRId32, dispatchMode); -} - void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) { // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no // valid pointer property from the input event. diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index b2966f680c..61b07feb3a 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -16,6 +16,7 @@ #pragma once +#include <ftl/flags.h> #include <gui/constants.h> #include <input/InputTransport.h> #include <ui/Transform.h> @@ -30,70 +31,70 @@ namespace android::inputdispatcher { * window area. */ struct InputTarget { - enum { + enum class Flags : uint32_t { /* This flag indicates that the event is being delivered to a foreground application. */ - FLAG_FOREGROUND = 1 << 0, + FOREGROUND = 1 << 0, /* This flag indicates that the MotionEvent falls within the area of the target * obscured by another visible window above it. The motion event should be * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - FLAG_WINDOW_IS_OBSCURED = 1 << 1, + WINDOW_IS_OBSCURED = 1 << 1, /* This flag indicates that a motion event is being split across multiple windows. */ - FLAG_SPLIT = 1 << 2, + SPLIT = 1 << 2, /* This flag indicates that the pointer coordinates dispatched to the application * will be zeroed out to avoid revealing information to an application. This is * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing * the same UID from watching all touches. */ - FLAG_ZERO_COORDS = 1 << 3, + ZERO_COORDS = 1 << 3, /* This flag indicates that the event should be sent as is. * Should always be set unless the event is to be transmuted. */ - FLAG_DISPATCH_AS_IS = 1 << 8, + DISPATCH_AS_IS = 1 << 8, /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside * of the area of this target and so should instead be delivered as an * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ - FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, + DISPATCH_AS_OUTSIDE = 1 << 9, /* This flag indicates that a hover sequence is starting in the given window. * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, + DISPATCH_AS_HOVER_ENTER = 1 << 10, /* This flag indicates that a hover event happened outside of a window which handled * previous hover events, signifying the end of the current hover sequence for that * window. * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, + DISPATCH_AS_HOVER_EXIT = 1 << 11, /* This flag indicates that the event should be canceled. * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips * outside of a window. */ - FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, + DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, /* This flag indicates that the event should be dispatched as an initial down. * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips * into a new window. */ - FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, - - /* Mask for all dispatch modes. */ - FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE | - FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT | - FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER, + DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, /* This flag indicates that the target of a MotionEvent is partly or wholly * obscured by another visible window above it. The motion event should be * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ - FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, - + WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, }; + /* Mask for all dispatch modes. */ + static constexpr const ftl::Flags<InputTarget::Flags> DISPATCH_MASK = + ftl::Flags<InputTarget::Flags>() | Flags::DISPATCH_AS_IS | Flags::DISPATCH_AS_OUTSIDE | + Flags::DISPATCH_AS_HOVER_ENTER | Flags::DISPATCH_AS_HOVER_EXIT | + Flags::DISPATCH_AS_SLIPPERY_EXIT | Flags::DISPATCH_AS_SLIPPERY_ENTER; + // The input channel to be targeted. std::shared_ptr<InputChannel> inputChannel; // Flags for the input target. - int32_t flags = 0; + ftl::Flags<Flags> flags; // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index f5b7cb88d9..ee7da93975 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -14,12 +14,14 @@ * limitations under the License. */ +#include <android-base/stringprintf.h> #include <gui/WindowInfo.h> #include "InputTarget.h" - #include "TouchState.h" +using namespace android::ftl::flag_operators; +using android::base::StringPrintf; using android::gui::WindowInfo; using android::gui::WindowInfoHandle; @@ -29,14 +31,15 @@ void TouchState::reset() { *this = TouchState(); } -void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags, - BitSet32 pointerIds, std::optional<nsecs_t> eventTime) { +void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, + ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds, + std::optional<nsecs_t> eventTime) { for (size_t i = 0; i < windows.size(); i++) { TouchedWindow& touchedWindow = windows[i]; if (touchedWindow.windowHandle == windowHandle) { touchedWindow.targetFlags |= targetFlags; - if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; + if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) { + touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_IS); } // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have // downTime set initially. Need to update existing window when an pointer is down for @@ -69,10 +72,10 @@ void TouchState::removeWindowByToken(const sp<IBinder>& token) { void TouchState::filterNonAsIsTouchWindows() { for (size_t i = 0; i < windows.size();) { TouchedWindow& window = windows[i]; - if (window.targetFlags & - (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) { - window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; - window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; + if (window.targetFlags.any(InputTarget::Flags::DISPATCH_AS_IS | + InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) { + window.targetFlags.clear(InputTarget::DISPATCH_MASK); + window.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS; i += 1; } else { windows.erase(windows.begin() + i); @@ -104,7 +107,7 @@ void TouchState::cancelPointersForNonPilferingWindows(const BitSet32 pointerIds) sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const { for (size_t i = 0; i < windows.size(); i++) { const TouchedWindow& window = windows[i]; - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) { return window.windowHandle; } } @@ -115,7 +118,7 @@ bool TouchState::isSlippery() const { // Must have exactly one foreground window. bool haveSlipperyForegroundWindow = false; for (const TouchedWindow& window : windows) { - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) { if (haveSlipperyForegroundWindow || !window.windowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::SLIPPERY)) { @@ -143,4 +146,19 @@ bool TouchState::isDown() const { [](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); }); } +std::string TouchState::dump() const { + std::string out; + out += StringPrintf("deviceId=%d, source=0x%08x\n", deviceId, source); + if (!windows.empty()) { + out += " Windows:\n"; + for (size_t i = 0; i < windows.size(); i++) { + const TouchedWindow& touchedWindow = windows[i]; + out += StringPrintf(" %zu : ", i) + touchedWindow.dump(); + } + } else { + out += " Windows: <none>\n"; + } + return out; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index d1d3e9a1bc..77c1cdf50a 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -16,7 +16,6 @@ #pragma once -#include "Monitor.h" #include "TouchedWindow.h" namespace android { @@ -41,7 +40,7 @@ struct TouchState { void reset(); void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, + ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds, std::optional<nsecs_t> eventTime = std::nullopt); void removeWindowByToken(const sp<IBinder>& token); void filterNonAsIsTouchWindows(); @@ -57,6 +56,7 @@ struct TouchState { sp<android::gui::WindowInfoHandle> getWallpaperWindow() const; // Whether any of the windows are currently being touched bool isDown() const; + std::string dump() const; }; } // namespace inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp new file mode 100644 index 0000000000..af745988ad --- /dev/null +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -0,0 +1,36 @@ +/* + * 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 "TouchedWindow.h" + +#include <android-base/stringprintf.h> +#include <input/PrintTools.h> + +using android::base::StringPrintf; + +namespace android { + +namespace inputdispatcher { + +std::string TouchedWindow::dump() const { + return StringPrintf("name='%s', pointerIds=0x%0x, " + "targetFlags=%s, firstDownTimeInTarget=%s\n", + windowHandle->getName().c_str(), pointerIds.value, + targetFlags.string().c_str(), toString(firstDownTimeInTarget).c_str()); +} + +} // namespace inputdispatcher +} // namespace android diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index a6c505b854..dd08323dd4 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -16,23 +16,24 @@ #pragma once -namespace android { +#include <gui/WindowInfo.h> +#include <utils/BitSet.h> +#include "InputTarget.h" -namespace gui { -class WindowInfoHandle; -} +namespace android { namespace inputdispatcher { // Focus tracking for touch. struct TouchedWindow { sp<gui::WindowInfoHandle> windowHandle; - int32_t targetFlags; + ftl::Flags<InputTarget::Flags> targetFlags; BitSet32 pointerIds; bool isPilferingPointers = false; // Time at which the first action down occurred on this window. // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario. std::optional<nsecs_t> firstDownTimeInTarget; + std::string dump() const; }; } // namespace inputdispatcher diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 647e10c974..7e0c1c77eb 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -79,6 +79,8 @@ public: POINTER, // Show spots and a spot anchor in place of the mouse pointer. SPOT, + + ftl_last = SPOT, }; /* Sets the mode of the pointer controller. */ diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index c691ca943f..a4f257c4b6 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -67,7 +67,7 @@ void CursorMotionAccumulator::finishSync() { // --- CursorInputMapper --- CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext) - : InputMapper(deviceContext) {} + : InputMapper(deviceContext), mLastEventTime(std::numeric_limits<nsecs_t>::min()) {} CursorInputMapper::~CursorInputMapper() { if (mPointerController != nullptr) { @@ -276,6 +276,7 @@ void CursorInputMapper::dumpParameters(std::string& dump) { std::list<NotifyArgs> CursorInputMapper::reset(nsecs_t when) { mButtonState = 0; mDownTime = 0; + mLastEventTime = std::numeric_limits<nsecs_t>::min(); mPointerVelocityControl.reset(); mWheelXVelocityControl.reset(); @@ -295,7 +296,11 @@ std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) { mCursorScrollAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - out += sync(rawEvent->when, rawEvent->readTime); + const nsecs_t eventTime = + applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), + rawEvent->when, mLastEventTime); + out += sync(eventTime, rawEvent->readTime); + mLastEventTime = eventTime; } return out; } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 6a4275ed54..20746e5bb0 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -121,6 +121,7 @@ private: int32_t mButtonState; nsecs_t mDownTime; + nsecs_t mLastEventTime; void configureParameters(); void dumpParameters(std::string& dump); diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 5a7ba9a6ed..0b7ff84c99 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -101,4 +101,30 @@ static bool isPointerDown(int32_t buttonState) { return out; } +// For devices connected over Bluetooth, although they may produce events at a consistent rate, +// the events might end up reaching Android in a "batched" manner through the Bluetooth +// stack, where a few events may be clumped together and processed around the same time. +// In this case, if the input device or its driver does not send or process the actual event +// generation timestamps, the event time will set to whenever the kernel received the event. +// When the timestamp deltas are minuscule for these batched events, any changes in x or y +// coordinates result in extremely large instantaneous velocities, which can negatively impact +// user experience. To avoid this, we augment the timestamps so that subsequent event timestamps +// differ by at least a minimum delta value. +static nsecs_t applyBluetoothTimestampSmoothening(const InputDeviceIdentifier& identifier, + nsecs_t currentEventTime, nsecs_t lastEventTime) { + if (identifier.bus != BUS_BLUETOOTH) { + return currentEventTime; + } + + // Assume the fastest rate at which a Bluetooth touch device can report input events is one + // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device + // will be separated by at least this amount. + constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); + // We define a maximum smoothing time delta so that we don't generate events too far into the + // future. + constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); + return std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA), + currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA); +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 3947cf7b9f..bf73ce5db8 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1469,6 +1469,9 @@ std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { const RawState& last = mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1]; + next.when = applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), when, + last.when); + // Assign pointer ids. if (!mHavePointerIds) { assignPointerIds(last, next); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index d5e4d5ae28..c20f28bc19 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -318,7 +318,7 @@ protected: RawPointerAxes mRawPointerAxes; struct RawState { - nsecs_t when{}; + nsecs_t when{std::numeric_limits<nsecs_t>::min()}; nsecs_t readTime{}; // Raw pointer sample data. diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index e4e22df5de..879d36e6fd 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -99,6 +99,11 @@ static constexpr int32_t ACTION_POINTER_1_UP = // Error tolerance for floating point assertions. static const float EPSILON = 0.001f; +// Minimum timestamp separation between subsequent input events from a Bluetooth device. +static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); +// Maximum smoothing time delta so that we don't generate events too far into the future. +constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); + template<typename T> static inline T min(T a, T b) { return a < b ? a : b; @@ -530,10 +535,11 @@ public: FakeEventHub() { } - void addDevice(int32_t deviceId, const std::string& name, - ftl::Flags<InputDeviceClass> classes) { + void addDevice(int32_t deviceId, const std::string& name, ftl::Flags<InputDeviceClass> classes, + int bus = 0) { Device* device = new Device(classes); device->identifier.name = name; + device->identifier.bus = bus; mDevices.add(deviceId, device); enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0); @@ -3255,13 +3261,13 @@ protected: std::unique_ptr<InstrumentedInputReader> mReader; std::shared_ptr<InputDevice> mDevice; - virtual void SetUp(ftl::Flags<InputDeviceClass> classes) { + virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0) { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = sp<FakeInputReaderPolicy>::make(); mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener); - mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); + mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus); // Consume the device reset notification generated when adding a new device. mFakeListener->assertNotifyDeviceResetWasCalled(); } @@ -3299,15 +3305,16 @@ protected: std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, const std::string& location, int32_t eventHubId, - ftl::Flags<InputDeviceClass> classes) { + ftl::Flags<InputDeviceClass> classes, int bus = 0) { InputDeviceIdentifier identifier; identifier.name = name; identifier.location = location; + identifier.bus = bus; std::shared_ptr<InputDevice> device = std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION, identifier); mReader->pushNextDevice(device); - mFakeEventHub->addDevice(eventHubId, name, classes); + mFakeEventHub->addDevice(eventHubId, name, classes, bus); mReader->loopOnce(); return device; } @@ -5486,6 +5493,106 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPoint ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } +// --- BluetoothCursorInputMapperTest --- + +class BluetoothCursorInputMapperTest : public CursorInputMapperTest { +protected: + void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); + + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + } +}; + +TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + } +} + +TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // Process several events with the same timestamp from the kernel. + // Ensure that we do not generate events too far into the future. + constexpr static int32_t numEvents = + MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; + for (int i = 0; i < numEvents; i++) { + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + } + + // By processing more events with the same timestamp, we should not generate events with a + // timestamp that is more than the specified max time delta from the timestamp at its injection. + const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; + for (int i = 0; i < 3; i++) { + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(cappedEventTime)))); + } +} + +TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp + // smoothening is not needed, its timestamp is not affected. + kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); + expectedEventTime = kernelEventTime; + + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); +} + // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { @@ -7479,7 +7586,8 @@ protected: void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value); void processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, int32_t value); void processMTSync(MultiTouchInputMapper& mapper); - void processSync(MultiTouchInputMapper& mapper); + void processSync(MultiTouchInputMapper& mapper, nsecs_t eventTime = ARBITRARY_TIME, + nsecs_t readTime = READ_TIME); }; void MultiTouchInputMapperTest::prepareAxes(int axes) { @@ -7592,8 +7700,9 @@ void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0); } -void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) { - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); +void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper, nsecs_t eventTime, + nsecs_t readTime) { + process(mapper, eventTime, readTime, EV_SYN, SYN_REPORT, 0); } TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) { @@ -10228,6 +10337,56 @@ TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) { ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } +// --- BluetoothMultiTouchInputMapperTest --- + +class BluetoothMultiTouchInputMapperTest : public MultiTouchInputMapperTest { +protected: + void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); + } +}; + +TEST_F(BluetoothMultiTouchInputMapperTest, TimestampSmoothening) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | PRESSURE); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + // Touch down. + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, 100, 200); + processPressure(mapper, RAW_PRESSURE_MAX); + processSync(mapper, ARBITRARY_TIME); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithEventTime(ARBITRARY_TIME)))); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + processPosition(mapper, 101 + i, 201 + i); + processSync(mapper, kernelEventTime); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithEventTime(expectedEventTime)))); + } + + // Release the touch. + processId(mapper, INVALID_TRACKING_ID); + processPressure(mapper, RAW_PRESSURE_MIN); + processSync(mapper, ARBITRARY_TIME + ms2ns(50)); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithEventTime(ARBITRARY_TIME + ms2ns(50))))); +} + +// --- MultiTouchPointerModeTest --- + class MultiTouchPointerModeTest : public MultiTouchInputMapperTest { protected: float mPointerMovementScale; diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h index 5107af7e27..9a47e3e4e7 100644 --- a/services/inputflinger/tests/TestInputListenerMatchers.h +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -91,4 +91,9 @@ MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") { return arg.buttonState == buttons; } +MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") { + *result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime; + return arg.eventTime == eventTime; +} + } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 0e41962afd..eff5130f5a 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -230,6 +230,8 @@ AidlComposer::AidlComposer(const std::string& serviceName) { return; } + addReader(translate<Display>(kSingleReaderKey)); + ALOGI("Loaded AIDL composer3 HAL service"); } @@ -298,12 +300,19 @@ void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { } } -void AidlComposer::resetCommands() { - mWriter.reset(); +void AidlComposer::resetCommands(Display display) { + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().reset(); + } + mMutex.unlock_shared(); } -Error AidlComposer::executeCommands() { - return execute(); +Error AidlComposer::executeCommands(Display display) { + mMutex.lock_shared(); + auto error = execute(display); + mMutex.unlock_shared(); + return error; } uint32_t AidlComposer::getMaxVirtualDisplayCount() { @@ -334,6 +343,7 @@ Error AidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelF *outDisplay = translate<Display>(virtualDisplay.display); *format = static_cast<PixelFormat>(virtualDisplay.format); + addDisplay(translate<Display>(virtualDisplay.display)); return Error::NONE; } @@ -343,12 +353,20 @@ Error AidlComposer::destroyVirtualDisplay(Display display) { ALOGE("destroyVirtualDisplay failed %s", status.getDescription().c_str()); return static_cast<Error>(status.getServiceSpecificError()); } + removeDisplay(display); return Error::NONE; } Error AidlComposer::acceptDisplayChanges(Display display) { - mWriter.acceptDisplayChanges(translate<int64_t>(display)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().acceptDisplayChanges(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::createLayer(Display display, Layer* outLayer) { @@ -388,7 +406,17 @@ Error AidlComposer::getActiveConfig(Display display, Config* outConfig) { Error AidlComposer::getChangedCompositionTypes( Display display, std::vector<Layer>* outLayers, std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) { - const auto changedLayers = mReader.takeChangedCompositionTypes(translate<int64_t>(display)); + std::vector<ChangedCompositionLayer> changedLayers; + Error error = Error::NONE; + { + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + changedLayers = reader->get().takeChangedCompositionTypes(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + } outLayers->reserve(changedLayers.size()); outTypes->reserve(changedLayers.size()); @@ -396,7 +424,7 @@ Error AidlComposer::getChangedCompositionTypes( outLayers->emplace_back(translate<Layer>(layer.layer)); outTypes->emplace_back(layer.composition); } - return Error::NONE; + return error; } Error AidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) { @@ -448,7 +476,17 @@ Error AidlComposer::getDisplayName(Display display, std::string* outName) { Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers, std::vector<uint32_t>* outLayerRequestMasks) { - const auto displayRequests = mReader.takeDisplayRequests(translate<int64_t>(display)); + Error error = Error::NONE; + DisplayRequest displayRequests; + { + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + displayRequests = reader->get().takeDisplayRequests(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + } *outDisplayRequestMask = translate<uint32_t>(displayRequests.mask); outLayers->reserve(displayRequests.layerRequests.size()); outLayerRequestMasks->reserve(displayRequests.layerRequests.size()); @@ -457,7 +495,7 @@ Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequ outLayers->emplace_back(translate<Layer>(layer.layer)); outLayerRequestMasks->emplace_back(translate<uint32_t>(layer.mask)); } - return Error::NONE; + return error; } Error AidlComposer::getDozeSupport(Display display, bool* outSupport) { @@ -511,7 +549,17 @@ Error AidlComposer::getOverlaySupport(AidlOverlayProperties* /*outProperties*/) Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) { - auto fences = mReader.takeReleaseFences(translate<int64_t>(display)); + Error error = Error::NONE; + std::vector<ReleaseFences::Layer> fences; + { + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + fences = reader->get().takeReleaseFences(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + } outLayers->reserve(fences.size()); outReleaseFences->reserve(fences.size()); @@ -522,19 +570,29 @@ Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLay *fence.fence.getR() = -1; outReleaseFences->emplace_back(fenceOwner); } - return Error::NONE; + return error; } Error AidlComposer::presentDisplay(Display display, int* outPresentFence) { ATRACE_NAME("HwcPresentDisplay"); - mWriter.presentDisplay(translate<int64_t>(display)); + Error error = Error::NONE; + mMutex.lock_shared(); + auto writer = getWriter(display); + auto reader = getReader(display); + if (writer && reader) { + writer->get().presentDisplay(translate<int64_t>(display)); + error = execute(display); + } else { + error = Error::BAD_DISPLAY; + } - Error error = execute(); if (error != Error::NONE) { + mMutex.unlock_shared(); return error; } - auto fence = mReader.takePresentFence(translate<int64_t>(display)); + auto fence = reader->get().takePresentFence(translate<int64_t>(display)); + mMutex.unlock_shared(); // take ownership *outPresentFence = fence.get(); *fence.getR() = -1; @@ -559,11 +617,19 @@ Error AidlComposer::setClientTarget(Display display, uint32_t slot, const sp<Gra handle = target->getNativeBuffer()->handle; } - mWriter.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence, - translate<aidl::android::hardware::graphics::common::Dataspace>( - dataspace), - translate<AidlRect>(damage)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get() + .setClientTarget(translate<int64_t>(display), slot, handle, acquireFence, + translate<aidl::android::hardware::graphics::common::Dataspace>( + dataspace), + translate<AidlRect>(damage)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) { @@ -579,14 +645,28 @@ Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent r } Error AidlComposer::setColorTransform(Display display, const float* matrix) { - mWriter.setColorTransform(translate<int64_t>(display), matrix); - return Error::NONE; + auto error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setColorTransform(translate<int64_t>(display), matrix); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer, int releaseFence) { - mWriter.setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence)); - return Error::NONE; + auto error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) { @@ -624,16 +704,26 @@ Error AidlComposer::setClientTargetSlotCount(Display display) { Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes, uint32_t* outNumRequests) { ATRACE_NAME("HwcValidateDisplay"); - mWriter.validateDisplay(translate<int64_t>(display), - ClockMonotonicTimestamp{expectedPresentTime}); + const auto displayId = translate<int64_t>(display); + Error error = Error::NONE; + mMutex.lock_shared(); + auto writer = getWriter(display); + auto reader = getReader(display); + if (writer && reader) { + writer->get().validateDisplay(displayId, ClockMonotonicTimestamp{expectedPresentTime}); + error = execute(display); + } else { + error = Error::BAD_DISPLAY; + } - Error error = execute(); if (error != Error::NONE) { + mMutex.unlock_shared(); return error; } - mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests); + reader->get().hasChanges(displayId, outNumTypes, outNumRequests); + mMutex.unlock_shared(); return Error::NONE; } @@ -641,39 +731,59 @@ Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPr uint32_t* outNumTypes, uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) { ATRACE_NAME("HwcPresentOrValidateDisplay"); - mWriter.presentOrvalidateDisplay(translate<int64_t>(display), - ClockMonotonicTimestamp{expectedPresentTime}); + const auto displayId = translate<int64_t>(display); + Error error = Error::NONE; + mMutex.lock_shared(); + auto writer = getWriter(display); + auto reader = getReader(display); + if (writer && reader) { + writer->get().presentOrvalidateDisplay(displayId, + ClockMonotonicTimestamp{expectedPresentTime}); + error = execute(display); + } else { + error = Error::BAD_DISPLAY; + } - Error error = execute(); if (error != Error::NONE) { + mMutex.unlock_shared(); return error; } - const auto result = mReader.takePresentOrValidateStage(translate<int64_t>(display)); + const auto result = reader->get().takePresentOrValidateStage(displayId); if (!result.has_value()) { *state = translate<uint32_t>(-1); + mMutex.unlock_shared(); return Error::NO_RESOURCES; } *state = translate<uint32_t>(*result); if (*result == PresentOrValidate::Result::Presented) { - auto fence = mReader.takePresentFence(translate<int64_t>(display)); + auto fence = reader->get().takePresentFence(displayId); // take ownership *outPresentFence = fence.get(); *fence.getR() = -1; } if (*result == PresentOrValidate::Result::Validated) { - mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests); + reader->get().hasChanges(displayId, outNumTypes, outNumRequests); } + mMutex.unlock_shared(); return Error::NONE; } Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) { - mWriter.setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), x, y); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), + x, y); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot, @@ -683,90 +793,190 @@ Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot, handle = buffer->getNativeBuffer()->handle; } - mWriter.setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, handle, - acquireFence); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, + handle, acquireFence); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer, const std::vector<IComposerClient::Rect>& damage) { - mWriter.setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlRect>(damage)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(damage)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) { - mWriter.setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer), - translate<BlendMode>(mode)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer), + translate<BlendMode>(mode)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerColor(Display display, Layer layer, const Color& color) { - mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerCompositionType( Display display, Layer layer, aidl::android::hardware::graphics::composer3::Composition type) { - mWriter.setLayerCompositionType(translate<int64_t>(display), translate<int64_t>(layer), type); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerCompositionType(translate<int64_t>(display), + translate<int64_t>(layer), type); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) { - mWriter.setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlDataspace>(dataspace)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlDataspace>(dataspace)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer, const IComposerClient::Rect& frame) { - mWriter.setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlRect>(frame)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(frame)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) { - mWriter.setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), alpha); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), + alpha); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerSidebandStream(Display display, Layer layer, const native_handle_t* stream) { - mWriter.setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), stream); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), + stream); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerSourceCrop(Display display, Layer layer, const IComposerClient::FRect& crop) { - mWriter.setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlFRect>(crop)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlFRect>(crop)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) { - mWriter.setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlTransform>(transform)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlTransform>(transform)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer, const std::vector<IComposerClient::Rect>& visible) { - mWriter.setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlRect>(visible)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(visible)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) { - mWriter.setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } -Error AidlComposer::execute() { - const auto& commands = mWriter.getPendingCommands(); +Error AidlComposer::execute(Display display) { + auto writer = getWriter(display); + auto reader = getReader(display); + if (!writer || !reader) { + return Error::BAD_DISPLAY; + } + + const auto& commands = writer->get().getPendingCommands(); if (commands.empty()) { - mWriter.reset(); + writer->get().reset(); return Error::NONE; } @@ -778,9 +988,9 @@ Error AidlComposer::execute() { return static_cast<Error>(status.getServiceSpecificError()); } - mReader.parse(std::move(results)); + reader->get().parse(std::move(results)); } - const auto commandErrors = mReader.takeErrors(); + const auto commandErrors = reader->get().takeErrors(); Error error = Error::NONE; for (const auto& cmdErr : commandErrors) { const auto index = static_cast<size_t>(cmdErr.commandIndex); @@ -798,7 +1008,7 @@ Error AidlComposer::execute() { } } - mWriter.reset(); + writer->get().reset(); return error; } @@ -806,9 +1016,17 @@ Error AidlComposer::execute() { Error AidlComposer::setLayerPerFrameMetadata( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) { - mWriter.setLayerPerFrameMetadata(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlPerFrameMetadata>(perFrameMetadatas)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPerFrameMetadata(translate<int64_t>(display), + translate<int64_t>(layer), + translate<AidlPerFrameMetadata>(perFrameMetadatas)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } std::vector<IComposerClient::PerFrameMetadataKey> AidlComposer::getPerFrameMetadataKeys( @@ -868,8 +1086,16 @@ Error AidlComposer::getDisplayIdentificationData(Display display, uint8_t* outPo } Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) { - mWriter.setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), matrix); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), + matrix); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat, @@ -932,20 +1158,36 @@ Error AidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrame Error AidlComposer::setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) { - mWriter.setLayerPerFrameMetadataBlobs(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlPerFrameMetadataBlob>(metadata)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPerFrameMetadataBlobs(translate<int64_t>(display), + translate<int64_t>(layer), + translate<AidlPerFrameMetadataBlob>(metadata)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setDisplayBrightness(Display display, float brightness, float brightnessNits, const DisplayBrightnessOptions& options) { - mWriter.setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits); - - if (options.applyImmediately) { - return execute(); + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits); + + if (options.applyImmediately) { + error = execute(display); + mMutex.unlock_shared(); + return error; + } + } else { + error = Error::BAD_DISPLAY; } - - return Error::NONE; + mMutex.unlock_shared(); + return error; } Error AidlComposer::getDisplayCapabilities(Display display, @@ -1085,20 +1327,43 @@ Error AidlComposer::getPreferredBootDisplayConfig(Display display, Config* confi Error AidlComposer::getClientTargetProperty( Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) { - *outClientTargetProperty = mReader.takeClientTargetProperty(translate<int64_t>(display)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + *outClientTargetProperty = + reader->get().takeClientTargetProperty(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) { - mWriter.setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer), brightness); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer), + brightness); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::setLayerBlockingRegion(Display display, Layer layer, const std::vector<IComposerClient::Rect>& blocking) { - mWriter.setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer), - translate<AidlRect>(blocking)); - return Error::NONE; + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer), + translate<AidlRect>(blocking)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; } Error AidlComposer::getDisplayDecorationSupport(Display display, @@ -1136,5 +1401,88 @@ Error AidlComposer::getPhysicalDisplayOrientation(Display displayId, return Error::NONE; } +ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display) + REQUIRES_SHARED(mMutex) { + return mWriters.get(display); +} + +ftl::Optional<std::reference_wrapper<ComposerClientReader>> AidlComposer::getReader(Display display) + REQUIRES_SHARED(mMutex) { + if (mSingleReader) { + display = translate<Display>(kSingleReaderKey); + } + return mReaders.get(display); +} + +void AidlComposer::removeDisplay(Display display) { + mMutex.lock(); + bool wasErased = mWriters.erase(display); + ALOGW_IF(!wasErased, + "Attempting to remove writer for display %" PRId64 " which is not connected", + translate<int64_t>(display)); + if (!mSingleReader) { + removeReader(display); + } + mMutex.unlock(); +} + +void AidlComposer::onHotplugDisconnect(Display display) { + removeDisplay(display); +} + +bool AidlComposer::hasMultiThreadedPresentSupport(Display display) { + const auto displayId = translate<int64_t>(display); + std::vector<AidlDisplayCapability> capabilities; + const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities); + if (!status.isOk()) { + ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str()); + return false; + } + return std::find(capabilities.begin(), capabilities.end(), + AidlDisplayCapability::MULTI_THREADED_PRESENT) != capabilities.end(); +} + +void AidlComposer::addReader(Display display) { + const auto displayId = translate<int64_t>(display); + std::optional<int64_t> displayOpt; + if (displayId != kSingleReaderKey) { + displayOpt.emplace(displayId); + } + auto [it, added] = mReaders.try_emplace(display, std::move(displayOpt)); + ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected", + displayId); +} + +void AidlComposer::removeReader(Display display) { + bool wasErased = mReaders.erase(display); + ALOGW_IF(!wasErased, + "Attempting to remove reader for display %" PRId64 " which is not connected", + translate<int64_t>(display)); +} + +void AidlComposer::addDisplay(Display display) { + const auto displayId = translate<int64_t>(display); + mMutex.lock(); + auto [it, added] = mWriters.try_emplace(display, displayId); + ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected", + displayId); + if (mSingleReader) { + if (hasMultiThreadedPresentSupport(display)) { + mSingleReader = false; + removeReader(translate<Display>(kSingleReaderKey)); + // Note that this includes the new display. + for (const auto& [existingDisplay, _] : mWriters) { + addReader(existingDisplay); + } + } + } else { + addReader(display); + } + mMutex.unlock(); +} + +void AidlComposer::onHotplugConnect(Display display) { + addDisplay(display); +} } // namespace Hwc2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index f2a59a5b95..d84efe7d38 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -17,10 +17,12 @@ #pragma once #include "ComposerHal.h" +#include <ftl/shared_mutex.h> +#include <ftl/small_map.h> +#include <functional> #include <optional> #include <string> -#include <unordered_map> #include <utility> #include <vector> @@ -70,10 +72,10 @@ public: // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. - void resetCommands() override; + void resetCommands(Display) override; // Explicitly flush all pending commands in the command buffer. - Error executeCommands() override; + Error executeCommands(Display) override; uint32_t getMaxVirtualDisplayCount() override; Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, @@ -228,16 +230,29 @@ public: Error getPhysicalDisplayOrientation(Display displayId, AidlTransform* outDisplayOrientation) override; + void onHotplugConnect(Display) override; + void onHotplugDisconnect(Display) override; private: // Many public functions above simply write a command into the command // queue to batch the calls. validateDisplay and presentDisplay will call // this function to execute the command queue. - Error execute(); + Error execute(Display) REQUIRES_SHARED(mMutex); // returns the default instance name for the given service static std::string instance(const std::string& serviceName); + ftl::Optional<std::reference_wrapper<ComposerClientWriter>> getWriter(Display) + REQUIRES_SHARED(mMutex); + ftl::Optional<std::reference_wrapper<ComposerClientReader>> getReader(Display) + REQUIRES_SHARED(mMutex); + void addDisplay(Display) EXCLUDES(mMutex); + void removeDisplay(Display) EXCLUDES(mMutex); + void addReader(Display) REQUIRES(mMutex); + void removeReader(Display) REQUIRES(mMutex); + + bool hasMultiThreadedPresentSupport(Display); + // 64KiB minus a small space for metadata such as read/write pointers static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16; // Max number of buffers that may be cached for a given layer @@ -245,8 +260,25 @@ private: // 1. Tightly coupling this cache to the max size of BufferQueue // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h) static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1; - ComposerClientWriter mWriter; - ComposerClientReader mReader; + + // Without DisplayCapability::MULTI_THREADED_PRESENT, we use a single reader + // for all displays. With the capability, we use a separate reader for each + // display. + bool mSingleReader = true; + // Invalid displayId used as a key to mReaders when mSingleReader is true. + static constexpr int64_t kSingleReaderKey = 0; + + // TODO (b/256881188): Use display::PhysicalDisplayMap instead of hard-coded `3` + ftl::SmallMap<Display, ComposerClientWriter, 3> mWriters GUARDED_BY(mMutex); + ftl::SmallMap<Display, ComposerClientReader, 3> mReaders GUARDED_BY(mMutex); + // Protect access to mWriters and mReaders with a shared_mutex. Adding and + // removing a display require exclusive access, since the iterator or the + // writer/reader may be invalidated. Other calls need shared access while + // using the writer/reader, so they can use their display's writer/reader + // without it being deleted or the iterator being invalidated. + // TODO (b/257958323): Use std::shared_mutex and RAII once they support + // threading annotations. + ftl::SharedMutex mMutex; // Aidl interface using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer; diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index b02f867b85..a9bf282d94 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -110,10 +110,10 @@ public: // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. - virtual void resetCommands() = 0; + virtual void resetCommands(Display) = 0; // Explicitly flush all pending commands in the command buffer. - virtual Error executeCommands() = 0; + virtual Error executeCommands(Display) = 0; virtual uint32_t getMaxVirtualDisplayCount() = 0; virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat*, @@ -283,6 +283,8 @@ public: virtual Error getPhysicalDisplayOrientation(Display displayId, AidlTransform* outDisplayOrientation) = 0; virtual Error getOverlaySupport(V3_0::OverlayProperties* outProperties) = 0; + virtual void onHotplugConnect(Display) = 0; + virtual void onHotplugDisconnect(Display) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 168e2ddabb..5f11cb8e30 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -513,7 +513,7 @@ status_t HWComposer::presentAndGetReleaseFences( if (displayData.validateWasSkipped) { // explicitly flush all pending commands - auto error = static_cast<hal::Error>(mComposer->executeCommands()); + auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId())); RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR); RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR); return NO_ERROR; @@ -933,6 +933,8 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( : "Secondary display", .deviceProductInfo = std::nullopt}; }(); + + mComposer->onHotplugConnect(hwcDisplayId); } if (!isConnected(info->id)) { @@ -960,6 +962,7 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect( // The display will later be destroyed by a call to HWComposer::disconnectDisplay. For now, mark // it as disconnected. mDisplayData.at(*displayId).hwcDisplay->setConnected(false); + mComposer->onHotplugDisconnect(hwcDisplayId); return DisplayIdentificationInfo{.id = *displayId}; } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index a664d2cf38..f8522e2ef0 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -273,11 +273,11 @@ void HidlComposer::registerCallback(const sp<IComposerCallback>& callback) { } } -void HidlComposer::resetCommands() { +void HidlComposer::resetCommands(Display) { mWriter.reset(); } -Error HidlComposer::executeCommands() { +Error HidlComposer::executeCommands(Display) { return execute(); } @@ -1357,6 +1357,9 @@ void HidlComposer::registerCallback(ComposerCallback& callback) { registerCallback(sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported)); } +void HidlComposer::onHotplugConnect(Display) {} +void HidlComposer::onHotplugDisconnect(Display) {} + CommandReader::~CommandReader() { resetData(); } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index b436408fb1..48b720c108 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -177,10 +177,10 @@ public: // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. - void resetCommands() override; + void resetCommands(Display) override; // Explicitly flush all pending commands in the command buffer. - Error executeCommands() override; + Error executeCommands(Display) override; uint32_t getMaxVirtualDisplayCount() override; Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, @@ -339,6 +339,8 @@ public: Error getPhysicalDisplayOrientation(Display displayId, AidlTransform* outDisplayOrientation) override; + void onHotplugConnect(Display) override; + void onHotplugDisconnect(Display) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp index f8fc6f5a40..8a6af10f58 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp @@ -326,8 +326,8 @@ void DisplayHardwareFuzzer::invokeAidlComposer() { invokeComposerHal2_3(&composer, display, outLayer); invokeComposerHal2_4(&composer, display, outLayer); - composer.executeCommands(); - composer.resetCommands(); + composer.executeCommands(display); + composer.resetCommands(display); composer.destroyLayer(display, outLayer); composer.destroyVirtualDisplay(display); diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 9d8e0a25e6..342c646847 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -73,6 +73,7 @@ struct HWComposerTest : testing::Test { EXPECT_CALL(*mHal, setClientTargetSlotCount(_)); EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE)); + EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId)); } }; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 3808487d0f..5ee38ec621 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -56,8 +56,8 @@ public: std::vector<aidl::android::hardware::graphics::composer3::Capability>()); MOCK_METHOD0(dumpDebugInfo, std::string()); MOCK_METHOD1(registerCallback, void(HWC2::ComposerCallback&)); - MOCK_METHOD0(resetCommands, void()); - MOCK_METHOD0(executeCommands, Error()); + MOCK_METHOD1(resetCommands, void(Display)); + MOCK_METHOD1(executeCommands, Error(Display)); MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t()); MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*)); MOCK_METHOD1(destroyVirtualDisplay, Error(Display)); @@ -166,6 +166,8 @@ public: MOCK_METHOD2(getPhysicalDisplayOrientation, Error(Display, AidlTransform*)); MOCK_METHOD1(getOverlaySupport, Error(aidl::android::hardware::graphics::composer3::OverlayProperties*)); + MOCK_METHOD1(onHotplugConnect, void(Display)); + MOCK_METHOD1(onHotplugDisconnect, void(Display)); }; } // namespace Hwc2::mock |