diff options
96 files changed, 2958 insertions, 1775 deletions
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index ebb78913b1..34ea7597b4 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -1956,7 +1956,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter, debuggable, boot_complete, for_restore, target_sdk_version, enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image, - compilation_reason); + background_job_compile, compilation_reason); bool cancelled = false; pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled); diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp index 51c4589440..4221a3a593 100644 --- a/cmds/installd/run_dex2oat.cpp +++ b/cmds/installd/run_dex2oat.cpp @@ -81,6 +81,7 @@ void RunDex2Oat::Initialize(const UniqueFile& output_oat, bool enable_hidden_api_checks, bool generate_compact_dex, bool use_jitzygote, + bool background_job_compile, const char* compilation_reason) { PrepareBootImageFlags(use_jitzygote); @@ -92,7 +93,8 @@ void RunDex2Oat::Initialize(const UniqueFile& output_oat, debuggable, target_sdk_version, enable_hidden_api_checks, generate_compact_dex, compilation_reason); - PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore); + PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore, + background_job_compile); const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", ""); std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags); @@ -296,7 +298,8 @@ void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex, } void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, - bool for_restore) { + bool for_restore, + bool background_job_compile) { // CPU set { std::string cpu_set_format = "--cpu-set=%s"; @@ -306,7 +309,12 @@ void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete "dalvik.vm.restore-dex2oat-cpu-set", "dalvik.vm.dex2oat-cpu-set", cpu_set_format) - : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)) + : (background_job_compile + ? MapPropertyToArgWithBackup( + "dalvik.vm.background-dex2oat-cpu-set", + "dalvik.vm.dex2oat-cpu-set", + cpu_set_format) + : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))) : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format); AddArg(dex2oat_cpu_set_arg); } @@ -320,7 +328,12 @@ void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete "dalvik.vm.restore-dex2oat-threads", "dalvik.vm.dex2oat-threads", threads_format) - : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)) + : (background_job_compile + ? MapPropertyToArgWithBackup( + "dalvik.vm.background-dex2oat-threads", + "dalvik.vm.dex2oat-threads", + threads_format) + : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))) : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format); AddArg(dex2oat_threads_arg); } diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h index 559244f2b7..c13e1f1acb 100644 --- a/cmds/installd/run_dex2oat.h +++ b/cmds/installd/run_dex2oat.h @@ -51,6 +51,7 @@ class RunDex2Oat { bool enable_hidden_api_checks, bool generate_compact_dex, bool use_jitzygote, + bool background_job_compile, const char* compilation_reason); void Exec(int exit_code); @@ -76,7 +77,9 @@ class RunDex2Oat { bool enable_hidden_api_checks, bool generate_compact_dex, const char* compilation_reason); - void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore); + void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, + bool for_restore, + bool background_job_compile); virtual std::string GetProperty(const std::string& key, const std::string& default_value); virtual bool GetBoolProperty(const std::string& key, bool default_value); diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp index 2a8135a037..304ba7b04f 100644 --- a/cmds/installd/run_dex2oat_test.cpp +++ b/cmds/installd/run_dex2oat_test.cpp @@ -115,6 +115,7 @@ class RunDex2OatTest : public testing::Test { bool enable_hidden_api_checks = false; bool generate_compact_dex = true; bool use_jitzygote = false; + bool background_job_compile = false; const char* compilation_reason = nullptr; }; @@ -259,6 +260,7 @@ class RunDex2OatTest : public testing::Test { args->enable_hidden_api_checks, args->generate_compact_dex, args->use_jitzygote, + args->background_job_compile, args->compilation_reason); runner.Exec(/*exit_code=*/ 0); } @@ -375,6 +377,30 @@ TEST_F(RunDex2OatTest, CpuSetPostBootCompleteNotForRestore) { VerifyExpectedFlags(); } +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground) { + setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", "1,3"); + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->background_job_compile = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,3"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground_Backup) { + setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", ""); + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->background_job_compile = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) { setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2"); setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3"); @@ -481,6 +507,30 @@ TEST_F(RunDex2OatTest, ThreadsPostBootCompleteNotForRestore) { VerifyExpectedFlags(); } +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground) { + setSystemProperty("dalvik.vm.background-dex2oat-threads", "2"); + setSystemProperty("dalvik.vm.dex2oat-threads", "3"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->background_job_compile = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground_Backup) { + setSystemProperty("dalvik.vm.background-dex2oat-threads", ""); + setSystemProperty("dalvik.vm.dex2oat-threads", "3"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->background_job_compile = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "3"); + VerifyExpectedFlags(); +} + TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) { setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4"); setSystemProperty("dalvik.vm.dex2oat-threads", "5"); diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 2684f048f8..2ae61b9603 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -612,7 +612,8 @@ void ServiceManager::binderDied(const wp<IBinder>& who) { } void ServiceManager::tryStartService(const std::string& name) { - ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service", + ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service. (if it's not " + "configured to be a lazy service, it may be stuck starting or still starting).", name.c_str()); std::thread([=] { diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 5fa47f64be..eed6b3339f 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -88,6 +88,36 @@ typedef struct APerformanceHintManager APerformanceHintManager; typedef struct APerformanceHintSession APerformanceHintSession; /** + * Hints for the session used by {@link APerformanceHint_sendHint} to signal upcoming changes + * in the mode or workload. + */ +enum SessionHint { + /** + * This hint indicates a sudden increase in CPU workload intensity. It means + * that this hint session needs extra CPU resources immediately to meet the + * target duration for the current work cycle. + */ + CPU_LOAD_UP = 0, + /** + * This hint indicates a decrease in CPU workload intensity. It means that + * this hint session can reduce CPU resources and still meet the target duration. + */ + CPU_LOAD_DOWN = 1, + /* + * This hint indicates an upcoming CPU workload that is completely changed and + * unknown. It means that the hint session should reset CPU resources to a known + * baseline to prepare for an arbitrary load, and must wake up if inactive. + */ + CPU_LOAD_RESET = 2, + /* + * This hint indicates that the most recent CPU workload is resuming after a + * period of inactivity. It means that the hint session should allocate similar + * CPU resources to what was used previously, and must wake up if inactive. + */ + CPU_LOAD_RESUME = 3, +}; + +/** * Acquire an instance of the performance hint manager. * * @return manager instance on success, nullptr on failure. @@ -159,6 +189,17 @@ int APerformanceHint_reportActualWorkDuration( void APerformanceHint_closeSession( APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__); +/** + * Sends performance hints to inform the hint session of changes in the workload. + * + * @param session The performance hint session instance to update. + * @param hint The hint to send to the session. + * @return 0 on success + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_sendHint( + APerformanceHintSession* session, int hint) __INTRODUCED_IN(__ANDROID_API_U__); + __END_DECLS #endif // ANDROID_NATIVE_PERFORMANCE_HINT_H 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/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/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h index fe5f35c266..d6937c2c52 100644 --- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h @@ -270,14 +270,19 @@ class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nul std::string getDescription() const { #ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ if (__builtin_available(android 30, *)) { -#else - if (__ANDROID_API__ >= 30) { #endif + +#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 30 const char* cStr = AStatus_getDescription(get()); std::string ret = cStr; AStatus_deleteDescription(cStr); return ret; +#endif + +#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ } +#endif + binder_exception_t exception = getExceptionCode(); std::string desc = std::to_string(exception); if (exception == EX_SERVICE_SPECIFIC) { diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h index c1f2620770..caee4711a4 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h @@ -41,68 +41,34 @@ enum { if (_status != STATUS_OK) return _status; \ } while (false) +// AParcelableHolder has been introduced in 31. +#if __ANDROID_API__ >= 31 class AParcelableHolder { public: AParcelableHolder() = delete; explicit AParcelableHolder(parcelable_stability_t stability) : mParcel(AParcel_create()), mStability(stability) {} -#if __ANDROID_API__ >= 31 AParcelableHolder(const AParcelableHolder& other) : mParcel(AParcel_create()), mStability(other.mStability) { - // AParcelableHolder has been introduced in 31. -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0, - AParcel_getDataSize(other.mParcel.get())); - } else { - syslog(LOG_ERR, - "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!"); - } + AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0, + AParcel_getDataSize(other.mParcel.get())); } -#endif AParcelableHolder(AParcelableHolder&& other) = default; virtual ~AParcelableHolder() = default; binder_status_t writeToParcel(AParcel* parcel) const { RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability))); -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - int32_t size = AParcel_getDataSize(this->mParcel.get()); - RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size)); - } else { - return STATUS_INVALID_OPERATION; - } -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - int32_t size = AParcel_getDataSize(this->mParcel.get()); - RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size)); - } else { - return STATUS_INVALID_OPERATION; - } + int32_t size = AParcel_getDataSize(this->mParcel.get()); + RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size)); + size = AParcel_getDataSize(this->mParcel.get()); + RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size)); return STATUS_OK; } binder_status_t readFromParcel(const AParcel* parcel) { -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - AParcel_reset(mParcel.get()); - } else { - return STATUS_INVALID_OPERATION; - } + AParcel_reset(mParcel.get()); parcelable_stability_t wireStability; RETURN_ON_FAILURE(AParcel_readInt32(parcel, &wireStability)); @@ -123,15 +89,7 @@ class AParcelableHolder { return STATUS_BAD_VALUE; } -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize); - } else { - status = STATUS_INVALID_OPERATION; - } + status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize); if (status != STATUS_OK) { return status; } @@ -143,15 +101,7 @@ class AParcelableHolder { if (this->mStability > T::_aidl_stability) { return STATUS_BAD_VALUE; } -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - AParcel_reset(mParcel.get()); - } else { - return STATUS_INVALID_OPERATION; - } + AParcel_reset(mParcel.get()); AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor)); p.writeToParcel(mParcel.get()); return STATUS_OK; @@ -161,17 +111,9 @@ class AParcelableHolder { binder_status_t getParcelable(std::optional<T>* ret) const { const std::string parcelableDesc(T::descriptor); AParcel_setDataPosition(mParcel.get(), 0); -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - if (AParcel_getDataSize(mParcel.get()) == 0) { - *ret = std::nullopt; - return STATUS_OK; - } - } else { - return STATUS_INVALID_OPERATION; + if (AParcel_getDataSize(mParcel.get()) == 0) { + *ret = std::nullopt; + return STATUS_OK; } std::string parcelableDescInParcel; binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel); @@ -188,18 +130,7 @@ class AParcelableHolder { return STATUS_OK; } - void reset() { -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - AParcel_reset(mParcel.get()); - } else { - syslog(LOG_ERR, - "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!"); - } - } + void reset() { AParcel_reset(mParcel.get()); } inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; } inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; } @@ -207,34 +138,23 @@ class AParcelableHolder { inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; } inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; } inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; } -#if __ANDROID_API__ >= 31 inline AParcelableHolder& operator=(const AParcelableHolder& rhs) { - // AParcelableHolder has been introduced in 31. -#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ - if (__builtin_available(android 31, *)) { -#else - if (__ANDROID_API__ >= 31) { -#endif - this->reset(); - if (this->mStability != rhs.mStability) { - syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!", - this->mStability, rhs.mStability); - abort(); - } - AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0, - AParcel_getDataSize(rhs.mParcel.get())); - } else { - syslog(LOG_ERR, - "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!"); - } + this->reset(); + if (this->mStability != rhs.mStability) { + syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!", + this->mStability, rhs.mStability); + abort(); + } + AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0, + AParcel_getDataSize(rhs.mParcel.get())); return *this; } -#endif private: mutable ndk::ScopedAParcel mParcel; parcelable_stability_t mStability; }; +#endif // __ANDROID_API__ >= 31 #undef RETURN_ON_FAILURE } // namespace ndk diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h index d7840ec1f0..6a25db220f 100644 --- a/libs/binder/ndk/include_cpp/android/binder_to_string.h +++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h @@ -136,8 +136,10 @@ class ToEmptyString { template <typename _U> static std::enable_if_t< #ifdef HAS_NDK_INTERFACE - std::is_base_of_v<::ndk::ICInterface, _U> || - std::is_same_v<::ndk::AParcelableHolder, _U> + std::is_base_of_v<::ndk::ICInterface, _U> +#if __ANDROID_API__ >= 31 + || std::is_same_v<::ndk::AParcelableHolder, _U> +#endif #else std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> || std::is_same_v<os::ParcelFileDescriptor, _U> || 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/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs index 21271d2484..c5c7719df2 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs +++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs @@ -22,7 +22,7 @@ extern crate libfuzzer_sys; mod read_utils; -use crate::read_utils::get_read_funcs; +use crate::read_utils::READ_FUNCS; use binder::binder_impl::{ Binder, BorrowedParcel, IBinderInternal, Parcel, Stability, TransactionCode, }; @@ -34,18 +34,18 @@ use binder_random_parcel_rs::create_random_parcel; use libfuzzer_sys::arbitrary::Arbitrary; #[derive(Arbitrary, Debug)] -enum ReadOperations { +enum ReadOperation { SetDataPosition { pos: i32 }, GetDataSize, ReadParcelableHolder { is_vintf: bool }, - ReadBasicTypes { indexes: Vec<usize> }, + ReadBasicTypes { instructions: Vec<usize> }, } #[derive(Arbitrary, Debug)] -enum Operations<'a> { +enum Operation<'a> { Transact { code: u32, flag: u32, data: &'a [u8] }, Append { start: i32, len: i32, data1: &'a [u8], data2: &'a [u8], append_all: bool }, - Read { indexes: Vec<ReadOperations>, data: &'a [u8] }, + Read { read_operations: Vec<ReadOperation>, data: &'a [u8] }, } /// Interface to fuzz transact with random parcel @@ -102,13 +102,12 @@ fn do_append_fuzz(start: i32, len: i32, data1: &[u8], data2: &[u8], append_all: }; } -fn do_read_fuzz(read_operations: Vec<ReadOperations>, data: &[u8]) { - let read_funcs = get_read_funcs(); +fn do_read_fuzz(read_operations: Vec<ReadOperation>, data: &[u8]) { let parcel = create_random_parcel(data); for operation in read_operations { match operation { - ReadOperations::SetDataPosition { pos } => { + ReadOperation::SetDataPosition { pos } => { unsafe { // Safety: Safe if pos is less than current size of the parcel. // It relies on C++ code for bound checks @@ -119,12 +118,12 @@ fn do_read_fuzz(read_operations: Vec<ReadOperations>, data: &[u8]) { } } - ReadOperations::GetDataSize => { + ReadOperation::GetDataSize => { let data_size = parcel.get_data_size(); println!("data size from parcel: {:?}", data_size); } - ReadOperations::ReadParcelableHolder { is_vintf } => { + ReadOperation::ReadParcelableHolder { is_vintf } => { let stability = if is_vintf { Stability::Vintf } else { Stability::Local }; let mut holder: ParcelableHolder = ParcelableHolder::new(stability); match holder.read_from_parcel(parcel.borrowed_ref()) { @@ -135,30 +134,28 @@ fn do_read_fuzz(read_operations: Vec<ReadOperations>, data: &[u8]) { } } - ReadOperations::ReadBasicTypes { indexes } => { - for index in indexes.iter() { - let read_index = index % read_funcs.len(); - read_funcs[read_index](parcel.borrowed_ref()); + ReadOperation::ReadBasicTypes { instructions } => { + for instruction in instructions.iter() { + let read_index = instruction % READ_FUNCS.len(); + READ_FUNCS[read_index](parcel.borrowed_ref()); } } } } } -fuzz_target!(|operations: Vec<Operations>| { - for operation in operations { - match operation { - Operations::Transact { code, flag, data } => { - do_transact(code, data, flag); - } +fuzz_target!(|operation: Operation| { + match operation { + Operation::Transact { code, flag, data } => { + do_transact(code, data, flag); + } - Operations::Append { start, len, data1, data2, append_all } => { - do_append_fuzz(start, len, data1, data2, append_all); - } + Operation::Append { start, len, data1, data2, append_all } => { + do_append_fuzz(start, len, data1, data2, append_all); + } - Operations::Read { indexes, data } => { - do_read_fuzz(indexes, data); - } + Operation::Read { read_operations, data } => { + do_read_fuzz(read_operations, data); } } }); diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs index 9b06013332..d2bfde1022 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs +++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs @@ -22,7 +22,7 @@ use binderReadParcelIface::aidl::SingleDataParcelable::SingleDataParcelable; macro_rules! read_parcel_interface { ($data_type:ty) => { - |parcel| { + |parcel: &BorrowedParcel<'_>| { let _res = parcel.read::<$data_type>(); } }; @@ -57,81 +57,77 @@ impl binder::Parcelable for SomeParcelable { binder::impl_deserialize_for_parcelable!(SomeParcelable); -pub fn get_read_funcs() -> Vec<Box<dyn Fn(&BorrowedParcel<'_>)>> { - let read_funcs: Vec<Box<dyn Fn(&BorrowedParcel<'_>)>> = vec![ - //read basic types - Box::new(read_parcel_interface!(bool)), - Box::new(read_parcel_interface!(i8)), - Box::new(read_parcel_interface!(i32)), - Box::new(read_parcel_interface!(i64)), - Box::new(read_parcel_interface!(f32)), - Box::new(read_parcel_interface!(f64)), - Box::new(read_parcel_interface!(u16)), - Box::new(read_parcel_interface!(u32)), - Box::new(read_parcel_interface!(u64)), - Box::new(read_parcel_interface!(String)), - //read vec of basic types - Box::new(read_parcel_interface!(Vec<i8>)), - Box::new(read_parcel_interface!(Vec<i32>)), - Box::new(read_parcel_interface!(Vec<i64>)), - Box::new(read_parcel_interface!(Vec<f32>)), - Box::new(read_parcel_interface!(Vec<f64>)), - Box::new(read_parcel_interface!(Vec<u16>)), - Box::new(read_parcel_interface!(Vec<u32>)), - Box::new(read_parcel_interface!(Vec<u64>)), - Box::new(read_parcel_interface!(Vec<String>)), - Box::new(read_parcel_interface!(Option<Vec<i8>>)), - Box::new(read_parcel_interface!(Option<Vec<i32>>)), - Box::new(read_parcel_interface!(Option<Vec<i64>>)), - Box::new(read_parcel_interface!(Option<Vec<f32>>)), - Box::new(read_parcel_interface!(Option<Vec<f64>>)), - Box::new(read_parcel_interface!(Option<Vec<u16>>)), - Box::new(read_parcel_interface!(Option<Vec<u32>>)), - Box::new(read_parcel_interface!(Option<Vec<u64>>)), - Box::new(read_parcel_interface!(Option<Vec<String>>)), - Box::new(read_parcel_interface!(ParcelFileDescriptor)), - Box::new(read_parcel_interface!(Vec<Option<ParcelFileDescriptor>>)), - Box::new(read_parcel_interface!(Option<Vec<ParcelFileDescriptor>>)), - Box::new(read_parcel_interface!(Option<Vec<Option<ParcelFileDescriptor>>>)), - Box::new(read_parcel_interface!(SpIBinder)), - Box::new(read_parcel_interface!(Vec<Option<SpIBinder>>)), - Box::new(read_parcel_interface!(Option<Vec<SpIBinder>>)), - Box::new(read_parcel_interface!(Option<Vec<Option<SpIBinder>>>)), - Box::new(read_parcel_interface!(SomeParcelable)), - Box::new(read_parcel_interface!(Vec<Option<SomeParcelable>>)), - Box::new(read_parcel_interface!(Option<Vec<SomeParcelable>>)), - Box::new(read_parcel_interface!(Option<Vec<Option<SomeParcelable>>>)), - // Fuzz read_from_parcel for AIDL generated parcelables - Box::new(|parcel| { - let mut empty_parcelable: EmptyParcelable = EmptyParcelable::default(); - match empty_parcelable.read_from_parcel(parcel) { - Ok(result) => result, - Err(e) => { - println!("EmptyParcelable: error occurred while reading from a parcel: {:?}", e) - } +pub const READ_FUNCS: &[fn(&BorrowedParcel<'_>)] = &[ + //read basic types + read_parcel_interface!(bool), + read_parcel_interface!(i8), + read_parcel_interface!(i32), + read_parcel_interface!(i64), + read_parcel_interface!(f32), + read_parcel_interface!(f64), + read_parcel_interface!(u16), + read_parcel_interface!(u32), + read_parcel_interface!(u64), + read_parcel_interface!(String), + //read vec of basic types + read_parcel_interface!(Vec<i8>), + read_parcel_interface!(Vec<i32>), + read_parcel_interface!(Vec<i64>), + read_parcel_interface!(Vec<f32>), + read_parcel_interface!(Vec<f64>), + read_parcel_interface!(Vec<u16>), + read_parcel_interface!(Vec<u32>), + read_parcel_interface!(Vec<u64>), + read_parcel_interface!(Vec<String>), + read_parcel_interface!(Option<Vec<i8>>), + read_parcel_interface!(Option<Vec<i32>>), + read_parcel_interface!(Option<Vec<i64>>), + read_parcel_interface!(Option<Vec<f32>>), + read_parcel_interface!(Option<Vec<f64>>), + read_parcel_interface!(Option<Vec<u16>>), + read_parcel_interface!(Option<Vec<u32>>), + read_parcel_interface!(Option<Vec<u64>>), + read_parcel_interface!(Option<Vec<String>>), + read_parcel_interface!(ParcelFileDescriptor), + read_parcel_interface!(Vec<Option<ParcelFileDescriptor>>), + read_parcel_interface!(Option<Vec<ParcelFileDescriptor>>), + read_parcel_interface!(Option<Vec<Option<ParcelFileDescriptor>>>), + read_parcel_interface!(SpIBinder), + read_parcel_interface!(Vec<Option<SpIBinder>>), + read_parcel_interface!(Option<Vec<SpIBinder>>), + read_parcel_interface!(Option<Vec<Option<SpIBinder>>>), + read_parcel_interface!(SomeParcelable), + read_parcel_interface!(Vec<Option<SomeParcelable>>), + read_parcel_interface!(Option<Vec<SomeParcelable>>), + read_parcel_interface!(Option<Vec<Option<SomeParcelable>>>), + // Fuzz read_from_parcel for AIDL generated parcelables + |parcel| { + let mut empty_parcelable: EmptyParcelable = EmptyParcelable::default(); + match empty_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => { + println!("EmptyParcelable: error occurred while reading from a parcel: {:?}", e) } - }), - Box::new(|parcel| { - let mut single_parcelable: SingleDataParcelable = SingleDataParcelable::default(); - match single_parcelable.read_from_parcel(parcel) { - Ok(result) => result, - Err(e) => println!( - "SingleDataParcelable: error occurred while reading from a parcel: {:?}", - e - ), - } - }), - Box::new(|parcel| { - let mut generic_parcelable: GenericDataParcelable = GenericDataParcelable::default(); - match generic_parcelable.read_from_parcel(parcel) { - Ok(result) => result, - Err(e) => println!( - "GenericDataParcelable: error occurred while reading from a parcel: {:?}", - e - ), - } - }), - ]; - - read_funcs -} + } + }, + |parcel| { + let mut single_parcelable: SingleDataParcelable = SingleDataParcelable::default(); + match single_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => println!( + "SingleDataParcelable: error occurred while reading from a parcel: {:?}", + e + ), + } + }, + |parcel| { + let mut generic_parcelable: GenericDataParcelable = GenericDataParcelable::default(); + match generic_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => println!( + "GenericDataParcelable: error occurred while reading from a parcel: {:?}", + e + ), + } + }, +]; diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 92d132f37c..03e4a23bd8 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -232,6 +232,7 @@ cc_defaults { srcs: [ "binderRpcTest.cpp", "binderRpcTestCommon.cpp", + "binderRpcUniversalTests.cpp", ], test_suites: ["general-tests"], 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 72943051c7..79bd9d4993 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -15,7 +15,6 @@ */ #include <android-base/stringprintf.h> -#include <gtest/gtest.h> #include <chrono> #include <cstdlib> @@ -29,6 +28,7 @@ #include <sys/socket.h> #include "binderRpcTestCommon.h" +#include "binderRpcTestFixture.h" using namespace std::chrono_literals; using namespace std::placeholders; @@ -44,37 +44,6 @@ constexpr bool kEnableSharedLibs = false; constexpr bool kEnableSharedLibs = true; #endif -static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT || - RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); - -TEST(BinderRpcParcel, EntireParcelFormatted) { - Parcel p; - p.writeInt32(3); - - EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "format must be set before data is written"); -} - -TEST(BinderRpc, CannotUseNextWireVersion) { - auto session = RpcSession::make(); - EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT)); - EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1)); - EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2)); - EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15)); -} - -TEST(BinderRpc, CanUseExperimentalWireVersion) { - auto session = RpcSession::make(); - EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL)); -} - -using android::binder::Status; - -#define EXPECT_OK(status) \ - do { \ - Status stat = (status); \ - EXPECT_TRUE(stat.isOk()) << stat; \ - } while (false) - static std::string WaitStatusToString(int wstatus) { if (WIFEXITED(wstatus)) { return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus)); @@ -92,7 +61,15 @@ static void debugBacktrace(pid_t pid) { class Process { public: - Process(Process&&) = default; + Process(Process&& other) + : mCustomExitStatusCheck(std::move(other.mCustomExitStatusCheck)), + mReadEnd(std::move(other.mReadEnd)), + mWriteEnd(std::move(other.mWriteEnd)) { + // The default move constructor doesn't clear mPid after moving it, + // which we need to do because the destructor checks for mPid!=0 + mPid = other.mPid; + other.mPid = 0; + } Process(const std::function<void(android::base::borrowed_fd /* writeEnd */, android::base::borrowed_fd /* readEnd */)>& f) { android::base::unique_fd childWriteEnd; @@ -152,21 +129,26 @@ static unsigned int allocateVsockPort() { return vsockPort++; } -struct ProcessSession { - // reference to process hosting a socket server - Process host; +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; +} - struct SessionInfo { - sp<RpcSession> session; - sp<IBinder> root; - }; +// Destructors need to be defined, even if pure virtual +ProcessSession::~ProcessSession() {} - // client session objects associated with other process - // each one represents a separate session - std::vector<SessionInfo> sessions; +class LinuxProcessSession : public ProcessSession { +public: + // reference to process hosting a socket server + Process host; - ProcessSession(ProcessSession&&) = default; - ~ProcessSession() { + LinuxProcessSession(LinuxProcessSession&&) = default; + LinuxProcessSession(Process&& host) : host(std::move(host)) {} + ~LinuxProcessSession() override { for (auto& session : sessions) { session.root = nullptr; } @@ -197,46 +179,12 @@ struct ProcessSession { } } } -}; - -// Process session where the process hosts IBinderRpcTest, the server used -// for most testing here -struct BinderRpcTestProcessSession { - ProcessSession proc; - - // pre-fetched root object (for first session) - sp<IBinder> rootBinder; - - // pre-casted root object (for first session) - sp<IBinderRpcTest> rootIface; - - // whether session should be invalidated by end of run - bool expectAlreadyShutdown = false; - BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; - ~BinderRpcTestProcessSession() { - if (!expectAlreadyShutdown) { - EXPECT_NE(nullptr, rootIface); - if (rootIface == nullptr) return; - - std::vector<int32_t> remoteCounts; - // calling over any sessions counts across all sessions - EXPECT_OK(rootIface->countBinders(&remoteCounts)); - EXPECT_EQ(remoteCounts.size(), proc.sessions.size()); - for (auto remoteCount : remoteCounts) { - EXPECT_EQ(remoteCount, 1); - } - - // even though it is on another thread, shutdown races with - // the transaction reply being written - if (auto status = rootIface->scheduleShutdown(); !status.isOk()) { - EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; - } - } - - rootIface = nullptr; - rootBinder = nullptr; + void setCustomExitStatusCheck(std::function<void(int wstatus)> f) override { + host.setCustomExitStatusCheck(std::move(f)); } + + void terminate() override { host.terminate(); } }; static base::unique_fd connectTo(const RpcSocketAddress& addr) { @@ -273,552 +221,137 @@ static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) return std::move(sockClient); } -using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd, - android::base::borrowed_fd readEnd); - -class BinderRpc : public ::testing::TestWithParam< - std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> { -public: - SocketType socketType() const { return std::get<0>(GetParam()); } - RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); } - uint32_t clientVersion() const { return std::get<2>(GetParam()); } - uint32_t serverVersion() const { return std::get<3>(GetParam()); } - bool serverSingleThreaded() const { return std::get<4>(GetParam()); } - bool noKernel() const { return std::get<5>(GetParam()); } - - bool clientOrServerSingleThreaded() const { - return !kEnableRpcThreads || serverSingleThreaded(); - } - - // Whether the test params support sending FDs in parcels. - bool supportsFdTransport() const { - return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS && - (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX || - socketType() == SocketType::UNIX_BOOTSTRAP); +std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { + auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param; + auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" + + std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion); + if (singleThreaded) { + ret += "_single_threaded"; } - - void SetUp() override { - if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) { - GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport"; - } + if (noKernel) { + ret += "_no_kernel"; } + return ret; +} - static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param; - auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" + - std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion); - if (singleThreaded) { - ret += "_single_threaded"; - } - if (noKernel) { - ret += "_no_kernel"; - } - return ret; +// This creates a new process serving an interface on a certain number of +// threads. +std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( + const BinderRpcOptions& options) { + CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server"; + + SocketType socketType = std::get<0>(GetParam()); + RpcSecurity rpcSecurity = std::get<1>(GetParam()); + uint32_t clientVersion = std::get<2>(GetParam()); + uint32_t serverVersion = std::get<3>(GetParam()); + bool singleThreaded = std::get<4>(GetParam()); + bool noKernel = std::get<5>(GetParam()); + + std::string path = android::base::GetExecutableDirectory(); + auto servicePath = android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(), + singleThreaded ? "_single_threaded" : "", + noKernel ? "_no_kernel" : ""); + + 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); } - - // This creates a new process serving an interface on a certain number of - // threads. - ProcessSession createRpcTestSocketServerProcessEtc(const BinderRpcOptions& options) { - CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server"; - - SocketType socketType = std::get<0>(GetParam()); - RpcSecurity rpcSecurity = std::get<1>(GetParam()); - uint32_t clientVersion = std::get<2>(GetParam()); - uint32_t serverVersion = std::get<3>(GetParam()); - bool singleThreaded = std::get<4>(GetParam()); - bool noKernel = std::get<5>(GetParam()); - - std::string path = android::base::GetExecutableDirectory(); - auto servicePath = - android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(), - singleThreaded ? "_single_threaded" : "", - noKernel ? "_no_kernel" : ""); - - base::unique_fd bootstrapClientFd, bootstrapServerFd; - // 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 ret = ProcessSession{ - .host = Process([=](android::base::borrowed_fd writeEnd, - android::base::borrowed_fd readEnd) { - auto writeFd = std::to_string(writeEnd.get()); - auto readFd = std::to_string(readEnd.get()); - execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(), - NULL); - }), - }; - - BinderRpcTestServerConfig serverConfig; - serverConfig.numThreads = options.numThreads; - serverConfig.socketType = static_cast<int32_t>(socketType); - serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity); - serverConfig.serverVersion = serverVersion; - serverConfig.vsockPort = allocateVsockPort(); - serverConfig.addr = allocateSocketAddress(); - serverConfig.unixBootstrapFd = bootstrapServerFd.get(); - for (auto mode : options.serverSupportedFileDescriptorTransportModes) { - serverConfig.serverSupportedFileDescriptorTransportModes.push_back( - static_cast<int32_t>(mode)); - } - writeToFd(ret.host.writeEnd(), serverConfig); - - std::vector<sp<RpcSession>> sessions; - auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); - for (size_t i = 0; i < options.numSessions; i++) { - sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier))); - } - - auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret.host.readEnd()); - BinderRpcTestClientInfo clientInfo; - for (const auto& session : sessions) { - auto& parcelableCert = clientInfo.certs.emplace_back(); - parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM); - } - writeToFd(ret.host.writeEnd(), clientInfo); - - CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max()); - if (socketType == SocketType::INET) { - CHECK_NE(0, serverInfo.port); - } - - if (rpcSecurity == RpcSecurity::TLS) { - const auto& serverCert = serverInfo.cert.data; - CHECK_EQ(OK, - certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, - serverCert)); - } - - status_t status; - - for (const auto& session : sessions) { - CHECK(session->setProtocolVersion(clientVersion)); - session->setMaxIncomingThreads(options.numIncomingConnections); - session->setMaxOutgoingThreads(options.numOutgoingConnections); - session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); - - switch (socketType) { - case SocketType::PRECONNECTED: - status = session->setupPreconnectedClient({}, [=]() { - return connectTo(UnixSocketAddress(serverConfig.addr.c_str())); - }); - break; - case SocketType::UNIX: - status = session->setupUnixDomainClient(serverConfig.addr.c_str()); - break; - case SocketType::UNIX_BOOTSTRAP: - status = session->setupUnixDomainSocketBootstrapClient( - base::unique_fd(dup(bootstrapClientFd.get()))); - break; - case SocketType::VSOCK: - status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort); - break; - case SocketType::INET: - status = session->setupInetClient("127.0.0.1", serverInfo.port); - break; - default: - LOG_ALWAYS_FATAL("Unknown socket type"); - } - if (options.allowConnectFailure && status != OK) { - ret.sessions.clear(); + 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) { + auto writeFd = std::to_string(writeEnd.get()); + auto readFd = std::to_string(readEnd.get()); + execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(), + NULL); + })); + + BinderRpcTestServerConfig serverConfig; + serverConfig.numThreads = options.numThreads; + serverConfig.socketType = static_cast<int32_t>(socketType); + serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity); + serverConfig.serverVersion = serverVersion; + serverConfig.vsockPort = allocateVsockPort(); + 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)); + } + writeToFd(ret->host.writeEnd(), serverConfig); + + std::vector<sp<RpcSession>> sessions; + auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); + for (size_t i = 0; i < options.numSessions; i++) { + sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier))); + } + + auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd()); + BinderRpcTestClientInfo clientInfo; + for (const auto& session : sessions) { + auto& parcelableCert = clientInfo.certs.emplace_back(); + parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM); + } + writeToFd(ret->host.writeEnd(), clientInfo); + + CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max()); + if (socketType == SocketType::INET) { + CHECK_NE(0, serverInfo.port); + } + + if (rpcSecurity == RpcSecurity::TLS) { + const auto& serverCert = serverInfo.cert.data; + CHECK_EQ(OK, + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert)); + } + + status_t status; + + for (const auto& session : sessions) { + CHECK(session->setProtocolVersion(clientVersion)); + session->setMaxIncomingThreads(options.numIncomingConnections); + session->setMaxOutgoingThreads(options.numOutgoingConnections); + session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); + + switch (socketType) { + case SocketType::PRECONNECTED: + status = session->setupPreconnectedClient({}, [=]() { + return connectTo(UnixSocketAddress(serverConfig.addr.c_str())); + }); break; - } - CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status); - ret.sessions.push_back({session, session->getRootObject()}); + case SocketType::UNIX_RAW: + case SocketType::UNIX: + status = session->setupUnixDomainClient(serverConfig.addr.c_str()); + break; + case SocketType::UNIX_BOOTSTRAP: + status = session->setupUnixDomainSocketBootstrapClient( + base::unique_fd(dup(bootstrapClientFd.get()))); + break; + case SocketType::VSOCK: + status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort); + break; + case SocketType::INET: + status = session->setupInetClient("127.0.0.1", serverInfo.port); + break; + default: + LOG_ALWAYS_FATAL("Unknown socket type"); } - return ret; - } - - BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) { - BinderRpcTestProcessSession ret{ - .proc = createRpcTestSocketServerProcessEtc(options), - }; - - ret.rootBinder = ret.proc.sessions.empty() ? nullptr : ret.proc.sessions.at(0).root; - ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); - - return ret; - } - - void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, - size_t sleepMs = 500); -}; - -TEST_P(BinderRpc, Ping) { - auto proc = createRpcTestSocketServerProcess({}); - ASSERT_NE(proc.rootBinder, nullptr); - EXPECT_EQ(OK, proc.rootBinder->pingBinder()); -} - -TEST_P(BinderRpc, GetInterfaceDescriptor) { - auto proc = createRpcTestSocketServerProcess({}); - ASSERT_NE(proc.rootBinder, nullptr); - EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor()); -} - -TEST_P(BinderRpc, MultipleSessions) { - if (serverSingleThreaded()) { - // Tests with multiple sessions require a multi-threaded service, - // but work fine on a single-threaded client - GTEST_SKIP() << "This test requires a multi-threaded service"; - } - - auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5}); - for (auto session : proc.proc.sessions) { - ASSERT_NE(nullptr, session.root); - EXPECT_EQ(OK, session.root->pingBinder()); - } -} - -TEST_P(BinderRpc, SeparateRootObject) { - if (serverSingleThreaded()) { - GTEST_SKIP() << "This test requires a multi-threaded service"; - } - - SocketType type = std::get<0>(GetParam()); - if (type == SocketType::PRECONNECTED || type == SocketType::UNIX || - type == SocketType::UNIX_BOOTSTRAP) { - // we can't get port numbers for unix sockets - return; - } - - auto proc = createRpcTestSocketServerProcess({.numSessions = 2}); - - int port1 = 0; - EXPECT_OK(proc.rootIface->getClientPort(&port1)); - - sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root); - int port2; - EXPECT_OK(rootIface2->getClientPort(&port2)); - - // we should have a different IBinderRpcTest object created for each - // session, because we use setPerSessionRootObject - EXPECT_NE(port1, port2); -} - -TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) { - auto proc = createRpcTestSocketServerProcess({}); - Parcel data; - Parcel reply; - EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0)); -} - -TEST_P(BinderRpc, AppendSeparateFormats) { - auto proc1 = createRpcTestSocketServerProcess({}); - auto proc2 = createRpcTestSocketServerProcess({}); - - Parcel pRaw; - - Parcel p1; - p1.markForBinder(proc1.rootBinder); - p1.writeInt32(3); - - EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize())); - EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize())); - - Parcel p2; - p2.markForBinder(proc2.rootBinder); - p2.writeInt32(7); - - EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize())); - EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize())); -} - -TEST_P(BinderRpc, UnknownTransaction) { - auto proc = createRpcTestSocketServerProcess({}); - Parcel data; - data.markForBinder(proc.rootBinder); - Parcel reply; - EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0)); -} - -TEST_P(BinderRpc, SendSomethingOneway) { - auto proc = createRpcTestSocketServerProcess({}); - EXPECT_OK(proc.rootIface->sendString("asdf")); -} - -TEST_P(BinderRpc, SendAndGetResultBack) { - auto proc = createRpcTestSocketServerProcess({}); - std::string doubled; - EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled)); - EXPECT_EQ("cool cool ", doubled); -} - -TEST_P(BinderRpc, SendAndGetResultBackBig) { - auto proc = createRpcTestSocketServerProcess({}); - std::string single = std::string(1024, 'a'); - std::string doubled; - EXPECT_OK(proc.rootIface->doubleString(single, &doubled)); - EXPECT_EQ(single + single, doubled); -} - -TEST_P(BinderRpc, InvalidNullBinderReturn) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> outBinder; - EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL); -} - -TEST_P(BinderRpc, CallMeBack) { - auto proc = createRpcTestSocketServerProcess({}); - - int32_t pingResult; - EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult)); - EXPECT_EQ(OK, pingResult); - - EXPECT_EQ(0, MyBinderRpcSession::gNum); -} - -TEST_P(BinderRpc, RepeatBinder) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> inBinder = new MyBinderRpcSession("foo"); - sp<IBinder> outBinder; - EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); - EXPECT_EQ(inBinder, outBinder); - - wp<IBinder> weak = inBinder; - inBinder = nullptr; - outBinder = nullptr; - - // Force reading a reply, to process any pending dec refs from the other - // process (the other process will process dec refs there before processing - // the ping here). - EXPECT_EQ(OK, proc.rootBinder->pingBinder()); - - EXPECT_EQ(nullptr, weak.promote()); - - EXPECT_EQ(0, MyBinderRpcSession::gNum); -} - -TEST_P(BinderRpc, RepeatTheirBinder) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinderRpcSession> session; - EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); - - sp<IBinder> inBinder = IInterface::asBinder(session); - sp<IBinder> outBinder; - EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); - EXPECT_EQ(inBinder, outBinder); - - wp<IBinder> weak = inBinder; - session = nullptr; - inBinder = nullptr; - outBinder = nullptr; - - // Force reading a reply, to process any pending dec refs from the other - // process (the other process will process dec refs there before processing - // the ping here). - EXPECT_EQ(OK, proc.rootBinder->pingBinder()); - - EXPECT_EQ(nullptr, weak.promote()); -} - -TEST_P(BinderRpc, RepeatBinderNull) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> outBinder; - EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder)); - EXPECT_EQ(nullptr, outBinder); -} - -TEST_P(BinderRpc, HoldBinder) { - auto proc = createRpcTestSocketServerProcess({}); - - IBinder* ptr = nullptr; - { - sp<IBinder> binder = new BBinder(); - ptr = binder.get(); - EXPECT_OK(proc.rootIface->holdBinder(binder)); - } - - sp<IBinder> held; - EXPECT_OK(proc.rootIface->getHeldBinder(&held)); - - EXPECT_EQ(held.get(), ptr); - - // stop holding binder, because we test to make sure references are cleaned - // up - EXPECT_OK(proc.rootIface->holdBinder(nullptr)); - // and flush ref counts - EXPECT_EQ(OK, proc.rootBinder->pingBinder()); -} - -// START TESTS FOR LIMITATIONS OF SOCKET BINDER -// These are behavioral differences form regular binder, where certain usecases -// aren't supported. - -TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) { - auto proc1 = createRpcTestSocketServerProcess({}); - auto proc2 = createRpcTestSocketServerProcess({}); - - sp<IBinder> outBinder; - EXPECT_EQ(INVALID_OPERATION, - proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError()); -} - -TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { - if (serverSingleThreaded()) { - GTEST_SKIP() << "This test requires a multi-threaded service"; - } - - auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2}); - - sp<IBinder> outBinder; - EXPECT_EQ(INVALID_OPERATION, - proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder) - .transactionError()); -} - -TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { - if (!kEnableKernelIpc || noKernel()) { - GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " - "at build time."; - } - - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager()); - sp<IBinder> outBinder; - EXPECT_EQ(INVALID_OPERATION, - proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError()); -} - -TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) { - if (!kEnableKernelIpc || noKernel()) { - GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " - "at build time."; - } - - auto proc = createRpcTestSocketServerProcess({}); - - // for historical reasons, IServiceManager interface only returns the - // exception code - EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED, - defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder)); -} - -// END TESTS FOR LIMITATIONS OF SOCKET BINDER - -TEST_P(BinderRpc, RepeatRootObject) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> outBinder; - EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder)); - EXPECT_EQ(proc.rootBinder, outBinder); -} - -TEST_P(BinderRpc, NestedTransactions) { - auto proc = createRpcTestSocketServerProcess({ - // Enable FD support because it uses more stack space and so represents - // something closer to a worst case scenario. - .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, - .serverSupportedFileDescriptorTransportModes = - {RpcSession::FileDescriptorTransportMode::UNIX}, - }); - - auto nastyNester = sp<MyBinderRpcTest>::make(); - EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10)); - - wp<IBinder> weak = nastyNester; - nastyNester = nullptr; - EXPECT_EQ(nullptr, weak.promote()); -} - -TEST_P(BinderRpc, SameBinderEquality) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> a; - EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); - - sp<IBinder> b; - EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); - - EXPECT_EQ(a, b); -} - -TEST_P(BinderRpc, SameBinderEqualityWeak) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinder> a; - EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); - wp<IBinder> weak = a; - a = nullptr; - - sp<IBinder> b; - EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); - - // this is the wrong behavior, since BpBinder - // doesn't implement onIncStrongAttempted - // but make sure there is no crash - EXPECT_EQ(nullptr, weak.promote()); - - GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder."; - - // In order to fix this: - // - need to have incStrongAttempted reflected across IPC boundary (wait for - // response to promote - round trip...) - // - sendOnLastWeakRef, to delete entries out of RpcState table - EXPECT_EQ(b, weak.promote()); -} - -#define expectSessions(expected, iface) \ - do { \ - int session; \ - EXPECT_OK((iface)->getNumOpenSessions(&session)); \ - EXPECT_EQ(expected, session); \ - } while (false) - -TEST_P(BinderRpc, SingleSession) { - auto proc = createRpcTestSocketServerProcess({}); - - sp<IBinderRpcSession> session; - EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); - std::string out; - EXPECT_OK(session->getName(&out)); - EXPECT_EQ("aoeu", out); - - expectSessions(1, proc.rootIface); - session = nullptr; - expectSessions(0, proc.rootIface); -} - -TEST_P(BinderRpc, ManySessions) { - auto proc = createRpcTestSocketServerProcess({}); - - std::vector<sp<IBinderRpcSession>> sessions; - - for (size_t i = 0; i < 15; i++) { - expectSessions(i, proc.rootIface); - sp<IBinderRpcSession> session; - EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session)); - sessions.push_back(session); - } - expectSessions(sessions.size(), proc.rootIface); - for (size_t i = 0; i < sessions.size(); i++) { - std::string out; - EXPECT_OK(sessions.at(i)->getName(&out)); - EXPECT_EQ(std::to_string(i), out); - } - expectSessions(sessions.size(), proc.rootIface); - - while (!sessions.empty()) { - sessions.pop_back(); - expectSessions(sessions.size(), proc.rootIface); + if (options.allowConnectFailure && status != OK) { + ret->sessions.clear(); + break; + } + CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status); + ret->sessions.push_back({session, session->getRootObject()}); } - expectSessions(0, proc.rootIface); -} - -size_t epochMillis() { - using std::chrono::duration_cast; - using std::chrono::milliseconds; - using std::chrono::seconds; - using std::chrono::system_clock; - return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); + return ret; } TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { @@ -857,8 +390,8 @@ TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { for (auto& t : ts) t.join(); } -void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, - size_t sleepMs) { +static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, + size_t sleepMs = 500) { size_t epochMsBefore = epochMillis(); std::vector<std::thread> ts; @@ -958,20 +491,6 @@ TEST_P(BinderRpc, OnewayStressTest) { saturateThreadPool(kNumServerThreads, proc.rootIface); } -TEST_P(BinderRpc, OnewayCallDoesNotWait) { - constexpr size_t kReallyLongTimeMs = 100; - constexpr size_t kSleepMs = kReallyLongTimeMs * 5; - - auto proc = createRpcTestSocketServerProcess({}); - - size_t epochMsBefore = epochMillis(); - - EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs)); - - size_t epochMsAfter = epochMillis(); - EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs); -} - TEST_P(BinderRpc, OnewayCallQueueingWithFds) { if (!supportsFdTransport()) { GTEST_SKIP() << "Would fail trivially (which is tested elsewhere)"; @@ -1057,7 +576,7 @@ TEST_P(BinderRpc, OnewayCallExhaustion) { // Build up oneway calls on the second session to make sure it terminates // and shuts down. The first session should be unaffected (proc destructor // checks the first session). - auto iface = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root); + auto iface = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root); std::vector<std::thread> threads; for (size_t i = 0; i < kNumClients; i++) { @@ -1085,66 +604,7 @@ TEST_P(BinderRpc, OnewayCallExhaustion) { // any pending commands). We need to erase this session from the record // here, so that the destructor for our session won't check that this // session is valid, but we still want it to test the other session. - proc.proc.sessions.erase(proc.proc.sessions.begin() + 1); -} - -TEST_P(BinderRpc, Callbacks) { - const static std::string kTestString = "good afternoon!"; - - for (bool callIsOneway : {true, false}) { - for (bool callbackIsOneway : {true, false}) { - for (bool delayed : {true, false}) { - if (clientOrServerSingleThreaded() && - (callIsOneway || callbackIsOneway || delayed)) { - // we have no incoming connections to receive the callback - continue; - } - - size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1; - auto proc = createRpcTestSocketServerProcess( - {.numThreads = 1, - .numSessions = 1, - .numIncomingConnections = numIncomingConnections}); - auto cb = sp<MyBinderRpcCallback>::make(); - - if (callIsOneway) { - EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed, - kTestString)); - } else { - EXPECT_OK( - proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString)); - } - - // if both transactions are synchronous and the response is sent back on the - // same thread, everything should have happened in a nested call. Otherwise, - // the callback will be processed on another thread. - if (callIsOneway || callbackIsOneway || delayed) { - using std::literals::chrono_literals::operator""s; - RpcMutexUniqueLock _l(cb->mMutex); - cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); - } - - EXPECT_EQ(cb->mValues.size(), 1) - << "callIsOneway: " << callIsOneway - << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed; - if (cb->mValues.empty()) continue; - EXPECT_EQ(cb->mValues.at(0), kTestString) - << "callIsOneway: " << callIsOneway - << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed; - - // since we are severing the connection, we need to go ahead and - // tell the server to shutdown and exit so that waitpid won't hang - if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { - EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; - } - - // since this session has an incoming connection w/ a threadpool, we - // need to manually shut it down - EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); - proc.expectAlreadyShutdown = true; - } - } - } + proc.proc->sessions.erase(proc.proc->sessions.begin() + 1); } TEST_P(BinderRpc, SingleDeathRecipient) { @@ -1177,7 +637,7 @@ TEST_P(BinderRpc, SingleDeathRecipient) { ASSERT_TRUE(dr->mCv.wait_for(lock, 100ms, [&]() { return dr->dead; })); // need to wait for the session to shutdown so we don't "Leak session" - EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true)); proc.expectAlreadyShutdown = true; } @@ -1205,7 +665,7 @@ TEST_P(BinderRpc, SingleDeathRecipientOnShutdown) { // Explicitly calling shutDownAndWait will cause the death recipients // to be called. - EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true)); std::unique_lock<std::mutex> lock(dr->mMtx); if (!dr->dead) { @@ -1213,8 +673,8 @@ TEST_P(BinderRpc, SingleDeathRecipientOnShutdown) { } EXPECT_TRUE(dr->dead) << "Failed to receive the death notification."; - proc.proc.host.terminate(); - proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + proc.proc->terminate(); + proc.proc->setCustomExitStatusCheck([](int wstatus) { EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) << "server process failed incorrectly: " << WaitStatusToString(wstatus); }); @@ -1259,18 +719,10 @@ TEST_P(BinderRpc, UnlinkDeathRecipient) { } // need to wait for the session to shutdown so we don't "Leak session" - EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true)); proc.expectAlreadyShutdown = true; } -TEST_P(BinderRpc, OnewayCallbackWithNoThread) { - auto proc = createRpcTestSocketServerProcess({}); - auto cb = sp<MyBinderRpcCallback>::make(); - - Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything"); - EXPECT_EQ(WOULD_BLOCK, status.transactionError()); -} - TEST_P(BinderRpc, Die) { for (bool doDeathCleanup : {true, false}) { auto proc = createRpcTestSocketServerProcess({}); @@ -1286,7 +738,7 @@ TEST_P(BinderRpc, Die) { EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError()) << "Do death cleanup: " << doDeathCleanup; - proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + proc.proc->setCustomExitStatusCheck([](int wstatus) { EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1) << "server process failed incorrectly: " << WaitStatusToString(wstatus); }); @@ -1316,7 +768,7 @@ TEST_P(BinderRpc, UseKernelBinderCallingId) { // second time! we catch the error :) EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError()); - proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + proc.proc->setCustomExitStatusCheck([](int wstatus) { EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT) << "server process failed incorrectly: " << WaitStatusToString(wstatus); }); @@ -1330,9 +782,9 @@ TEST_P(BinderRpc, FileDescriptorTransportRejectNone) { {RpcSession::FileDescriptorTransportMode::UNIX}, .allowConnectFailure = true, }); - EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed"; - proc.proc.host.terminate(); - proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(proc.proc->sessions.empty()) << "session connections should have failed"; + proc.proc->terminate(); + proc.proc->setCustomExitStatusCheck([](int wstatus) { EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) << "server process failed incorrectly: " << WaitStatusToString(wstatus); }); @@ -1346,9 +798,9 @@ TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) { {RpcSession::FileDescriptorTransportMode::NONE}, .allowConnectFailure = true, }); - EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed"; - proc.proc.host.terminate(); - proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(proc.proc->sessions.empty()) << "session connections should have failed"; + proc.proc->terminate(); + proc.proc->setCustomExitStatusCheck([](int wstatus) { EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) << "server process failed incorrectly: " << WaitStatusToString(wstatus); }); @@ -1516,16 +968,6 @@ TEST_P(BinderRpc, Fds) { ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?"); } -TEST_P(BinderRpc, AidlDelegatorTest) { - auto proc = createRpcTestSocketServerProcess({}); - auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface); - ASSERT_NE(nullptr, myDelegator); - - std::string doubled; - EXPECT_OK(myDelegator->doubleString("cool ", &doubled)); - EXPECT_EQ("cool cool ", doubled); -} - static bool testSupportVsockLoopback() { // We don't need to enable TLS to know if vsock is supported. unsigned int vsockPort = allocateVsockPort(); @@ -1616,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); @@ -1858,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 823bbf6b7f..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: @@ -91,6 +94,14 @@ static inline std::string PrintToString(SocketType socketType) { } } +static inline size_t epochMillis() { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::seconds; + using std::chrono::system_clock; + return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); +} + struct BinderRpcOptions { size_t numThreads = 1; size_t numSessions = 1; diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h new file mode 100644 index 0000000000..5a78782b9b --- /dev/null +++ b/libs/binder/tests/binderRpcTestFixture.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gtest/gtest.h> + +#include "binderRpcTestCommon.h" + +#define EXPECT_OK(status) \ + do { \ + android::binder::Status stat = (status); \ + EXPECT_TRUE(stat.isOk()) << stat; \ + } while (false) + +namespace android { + +// Abstract base class with a virtual destructor that handles the +// ownership of a process session for BinderRpcTestSession below +class ProcessSession { +public: + struct SessionInfo { + sp<RpcSession> session; + sp<IBinder> root; + }; + + // client session objects associated with other process + // each one represents a separate session + std::vector<SessionInfo> sessions; + + virtual ~ProcessSession() = 0; + + // If the process exits with a status, run the given callback on that value. + virtual void setCustomExitStatusCheck(std::function<void(int wstatus)> f) = 0; + + // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead. + virtual void terminate() = 0; +}; + +// Process session where the process hosts IBinderRpcTest, the server used +// for most testing here +struct BinderRpcTestProcessSession { + std::unique_ptr<ProcessSession> proc; + + // pre-fetched root object (for first session) + sp<IBinder> rootBinder; + + // pre-casted root object (for first session) + sp<IBinderRpcTest> rootIface; + + // whether session should be invalidated by end of run + bool expectAlreadyShutdown = false; + + BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; + ~BinderRpcTestProcessSession() { + if (!expectAlreadyShutdown) { + EXPECT_NE(nullptr, rootIface); + if (rootIface == nullptr) return; + + std::vector<int32_t> remoteCounts; + // calling over any sessions counts across all sessions + EXPECT_OK(rootIface->countBinders(&remoteCounts)); + EXPECT_EQ(remoteCounts.size(), proc->sessions.size()); + for (auto remoteCount : remoteCounts) { + EXPECT_EQ(remoteCount, 1); + } + + // even though it is on another thread, shutdown races with + // the transaction reply being written + if (auto status = rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + } + + rootIface = nullptr; + rootBinder = nullptr; + } +}; + +class BinderRpc : public ::testing::TestWithParam< + std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> { +public: + SocketType socketType() const { return std::get<0>(GetParam()); } + RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); } + uint32_t clientVersion() const { return std::get<2>(GetParam()); } + uint32_t serverVersion() const { return std::get<3>(GetParam()); } + bool serverSingleThreaded() const { return std::get<4>(GetParam()); } + bool noKernel() const { return std::get<5>(GetParam()); } + + bool clientOrServerSingleThreaded() const { + return !kEnableRpcThreads || serverSingleThreaded(); + } + + // Whether the test params support sending FDs in parcels. + bool supportsFdTransport() const { + return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS && + (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX || + socketType() == SocketType::UNIX_BOOTSTRAP || + socketType() == SocketType::UNIX_RAW); + } + + void SetUp() override { + if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) { + GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport"; + } + } + + BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) { + BinderRpcTestProcessSession ret{ + .proc = createRpcTestSocketServerProcessEtc(options), + }; + + ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root; + ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); + + return ret; + } + + static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info); + +protected: + std::unique_ptr<ProcessSession> createRpcTestSocketServerProcessEtc( + const BinderRpcOptions& options); +}; + +} // namespace android 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 new file mode 100644 index 0000000000..f960442859 --- /dev/null +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <chrono> +#include <cstdlib> +#include <type_traits> + +#include "binderRpcTestCommon.h" +#include "binderRpcTestFixture.h" + +using namespace std::chrono_literals; +using namespace std::placeholders; +using testing::AssertionFailure; +using testing::AssertionResult; +using testing::AssertionSuccess; + +namespace android { + +static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT || + RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); + +TEST(BinderRpcParcel, EntireParcelFormatted) { + Parcel p; + p.writeInt32(3); + + EXPECT_DEATH_IF_SUPPORTED(p.markForBinder(sp<BBinder>::make()), + "format must be set before data is written"); +} + +TEST(BinderRpc, CannotUseNextWireVersion) { + auto session = RpcSession::make(); + EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT)); + EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1)); + EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2)); + EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15)); +} + +TEST(BinderRpc, CanUseExperimentalWireVersion) { + auto session = RpcSession::make(); + EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL)); +} + +TEST_P(BinderRpc, Ping) { + auto proc = createRpcTestSocketServerProcess({}); + ASSERT_NE(proc.rootBinder, nullptr); + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); +} + +TEST_P(BinderRpc, GetInterfaceDescriptor) { + auto proc = createRpcTestSocketServerProcess({}); + ASSERT_NE(proc.rootBinder, nullptr); + EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor()); +} + +TEST_P(BinderRpc, MultipleSessions) { + if (serverSingleThreaded()) { + // Tests with multiple sessions require a multi-threaded service, + // but work fine on a single-threaded client + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + + auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5}); + for (auto session : proc.proc->sessions) { + ASSERT_NE(nullptr, session.root); + EXPECT_EQ(OK, session.root->pingBinder()); + } +} + +TEST_P(BinderRpc, SeparateRootObject) { + if (serverSingleThreaded()) { + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + + SocketType type = std::get<0>(GetParam()); + if (type == SocketType::PRECONNECTED || type == SocketType::UNIX || + type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) { + // we can't get port numbers for unix sockets + return; + } + + auto proc = createRpcTestSocketServerProcess({.numSessions = 2}); + + int port1 = 0; + EXPECT_OK(proc.rootIface->getClientPort(&port1)); + + sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root); + int port2; + EXPECT_OK(rootIface2->getClientPort(&port2)); + + // we should have a different IBinderRpcTest object created for each + // session, because we use setPerSessionRootObject + EXPECT_NE(port1, port2); +} + +TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) { + auto proc = createRpcTestSocketServerProcess({}); + Parcel data; + Parcel reply; + EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0)); +} + +TEST_P(BinderRpc, AppendSeparateFormats) { + auto proc1 = createRpcTestSocketServerProcess({}); + auto proc2 = createRpcTestSocketServerProcess({}); + + Parcel pRaw; + + Parcel p1; + p1.markForBinder(proc1.rootBinder); + p1.writeInt32(3); + + EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize())); + EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize())); + + Parcel p2; + p2.markForBinder(proc2.rootBinder); + p2.writeInt32(7); + + EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize())); + EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize())); +} + +TEST_P(BinderRpc, UnknownTransaction) { + auto proc = createRpcTestSocketServerProcess({}); + Parcel data; + data.markForBinder(proc.rootBinder); + Parcel reply; + EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0)); +} + +TEST_P(BinderRpc, SendSomethingOneway) { + auto proc = createRpcTestSocketServerProcess({}); + EXPECT_OK(proc.rootIface->sendString("asdf")); +} + +TEST_P(BinderRpc, SendAndGetResultBack) { + auto proc = createRpcTestSocketServerProcess({}); + std::string doubled; + EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled)); + EXPECT_EQ("cool cool ", doubled); +} + +TEST_P(BinderRpc, SendAndGetResultBackBig) { + auto proc = createRpcTestSocketServerProcess({}); + std::string single = std::string(1024, 'a'); + std::string doubled; + EXPECT_OK(proc.rootIface->doubleString(single, &doubled)); + EXPECT_EQ(single + single, doubled); +} + +TEST_P(BinderRpc, InvalidNullBinderReturn) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL); +} + +TEST_P(BinderRpc, CallMeBack) { + auto proc = createRpcTestSocketServerProcess({}); + + int32_t pingResult; + EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult)); + EXPECT_EQ(OK, pingResult); + + EXPECT_EQ(0, MyBinderRpcSession::gNum); +} + +TEST_P(BinderRpc, RepeatBinder) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> inBinder = new MyBinderRpcSession("foo"); + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); + EXPECT_EQ(inBinder, outBinder); + + wp<IBinder> weak = inBinder; + inBinder = nullptr; + outBinder = nullptr; + + // Force reading a reply, to process any pending dec refs from the other + // process (the other process will process dec refs there before processing + // the ping here). + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + EXPECT_EQ(nullptr, weak.promote()); + + EXPECT_EQ(0, MyBinderRpcSession::gNum); +} + +TEST_P(BinderRpc, RepeatTheirBinder) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); + + sp<IBinder> inBinder = IInterface::asBinder(session); + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); + EXPECT_EQ(inBinder, outBinder); + + wp<IBinder> weak = inBinder; + session = nullptr; + inBinder = nullptr; + outBinder = nullptr; + + // Force reading a reply, to process any pending dec refs from the other + // process (the other process will process dec refs there before processing + // the ping here). + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + EXPECT_EQ(nullptr, weak.promote()); +} + +TEST_P(BinderRpc, RepeatBinderNull) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder)); + EXPECT_EQ(nullptr, outBinder); +} + +TEST_P(BinderRpc, HoldBinder) { + auto proc = createRpcTestSocketServerProcess({}); + + IBinder* ptr = nullptr; + { + sp<IBinder> binder = new BBinder(); + ptr = binder.get(); + EXPECT_OK(proc.rootIface->holdBinder(binder)); + } + + sp<IBinder> held; + EXPECT_OK(proc.rootIface->getHeldBinder(&held)); + + EXPECT_EQ(held.get(), ptr); + + // stop holding binder, because we test to make sure references are cleaned + // up + EXPECT_OK(proc.rootIface->holdBinder(nullptr)); + // and flush ref counts + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); +} + +// START TESTS FOR LIMITATIONS OF SOCKET BINDER +// These are behavioral differences form regular binder, where certain usecases +// aren't supported. + +TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) { + auto proc1 = createRpcTestSocketServerProcess({}); + auto proc2 = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError()); +} + +TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { + if (serverSingleThreaded()) { + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + + auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2}); + + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc.rootIface->repeatBinder(proc.proc->sessions.at(1).root, &outBinder) + .transactionError()); +} + +TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { + if (!kEnableKernelIpc || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager()); + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError()); +} + +TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) { + if (!kEnableKernelIpc || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + + auto proc = createRpcTestSocketServerProcess({}); + + // for historical reasons, IServiceManager interface only returns the + // exception code + EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED, + defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder)); +} + +// END TESTS FOR LIMITATIONS OF SOCKET BINDER + +TEST_P(BinderRpc, RepeatRootObject) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder)); + EXPECT_EQ(proc.rootBinder, outBinder); +} + +TEST_P(BinderRpc, NestedTransactions) { + auto proc = createRpcTestSocketServerProcess({ + // Enable FD support because it uses more stack space and so represents + // something closer to a worst case scenario. + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + auto nastyNester = sp<MyBinderRpcTest>::make(); + EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10)); + + wp<IBinder> weak = nastyNester; + nastyNester = nullptr; + EXPECT_EQ(nullptr, weak.promote()); +} + +TEST_P(BinderRpc, SameBinderEquality) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> a; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); + + sp<IBinder> b; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); + + EXPECT_EQ(a, b); +} + +TEST_P(BinderRpc, SameBinderEqualityWeak) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> a; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); + wp<IBinder> weak = a; + a = nullptr; + + sp<IBinder> b; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); + + // this is the wrong behavior, since BpBinder + // doesn't implement onIncStrongAttempted + // but make sure there is no crash + EXPECT_EQ(nullptr, weak.promote()); + + GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder."; + + // In order to fix this: + // - need to have incStrongAttempted reflected across IPC boundary (wait for + // response to promote - round trip...) + // - sendOnLastWeakRef, to delete entries out of RpcState table + EXPECT_EQ(b, weak.promote()); +} + +#define expectSessions(expected, iface) \ + do { \ + int session; \ + EXPECT_OK((iface)->getNumOpenSessions(&session)); \ + EXPECT_EQ(expected, session); \ + } while (false) + +TEST_P(BinderRpc, SingleSession) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); + std::string out; + EXPECT_OK(session->getName(&out)); + EXPECT_EQ("aoeu", out); + + expectSessions(1, proc.rootIface); + session = nullptr; + expectSessions(0, proc.rootIface); +} + +TEST_P(BinderRpc, ManySessions) { + auto proc = createRpcTestSocketServerProcess({}); + + std::vector<sp<IBinderRpcSession>> sessions; + + for (size_t i = 0; i < 15; i++) { + expectSessions(i, proc.rootIface); + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session)); + sessions.push_back(session); + } + expectSessions(sessions.size(), proc.rootIface); + for (size_t i = 0; i < sessions.size(); i++) { + std::string out; + EXPECT_OK(sessions.at(i)->getName(&out)); + EXPECT_EQ(std::to_string(i), out); + } + expectSessions(sessions.size(), proc.rootIface); + + while (!sessions.empty()) { + sessions.pop_back(); + expectSessions(sessions.size(), proc.rootIface); + } + expectSessions(0, proc.rootIface); +} + +TEST_P(BinderRpc, OnewayCallDoesNotWait) { + constexpr size_t kReallyLongTimeMs = 100; + constexpr size_t kSleepMs = kReallyLongTimeMs * 5; + + auto proc = createRpcTestSocketServerProcess({}); + + size_t epochMsBefore = epochMillis(); + + EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs)); + + size_t epochMsAfter = epochMillis(); + EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs); +} + +TEST_P(BinderRpc, Callbacks) { + const static std::string kTestString = "good afternoon!"; + + for (bool callIsOneway : {true, false}) { + for (bool callbackIsOneway : {true, false}) { + for (bool delayed : {true, false}) { + if (clientOrServerSingleThreaded() && + (callIsOneway || callbackIsOneway || delayed)) { + // we have no incoming connections to receive the callback + continue; + } + + size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1; + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, + .numSessions = 1, + .numIncomingConnections = numIncomingConnections}); + auto cb = sp<MyBinderRpcCallback>::make(); + + if (callIsOneway) { + EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed, + kTestString)); + } else { + EXPECT_OK( + proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString)); + } + + // if both transactions are synchronous and the response is sent back on the + // same thread, everything should have happened in a nested call. Otherwise, + // the callback will be processed on another thread. + if (callIsOneway || callbackIsOneway || delayed) { + using std::literals::chrono_literals::operator""s; + RpcMutexUniqueLock _l(cb->mMutex); + cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); + } + + EXPECT_EQ(cb->mValues.size(), 1) + << "callIsOneway: " << callIsOneway + << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed; + if (cb->mValues.empty()) continue; + EXPECT_EQ(cb->mValues.at(0), kTestString) + << "callIsOneway: " << callIsOneway + << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed; + + // since we are severing the connection, we need to go ahead and + // tell the server to shutdown and exit so that waitpid won't hang + if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + + // since this session has an incoming connection w/ a threadpool, we + // need to manually shut it down + EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true)); + proc.expectAlreadyShutdown = true; + } + } + } +} + +TEST_P(BinderRpc, OnewayCallbackWithNoThread) { + auto proc = createRpcTestSocketServerProcess({}); + auto cb = sp<MyBinderRpcCallback>::make(); + + Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything"); + EXPECT_EQ(WOULD_BLOCK, status.transactionError()); +} + +TEST_P(BinderRpc, AidlDelegatorTest) { + auto proc = createRpcTestSocketServerProcess({}); + auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface); + ASSERT_NE(nullptr, myDelegator); + + std::string doubled; + EXPECT_OK(myDelegator->doubleString("cool ", &doubled)); + EXPECT_EQ("cool cool ", doubled); +} + +} // namespace android diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index a96a07a9b8..af50a2980c 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -27,10 +27,13 @@ cc_library_shared { srcs: [ "GpuStatsInfo.cpp", "GraphicsEnv.cpp", - "IGpuService.cpp" + "IGpuService.cpp", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", @@ -46,4 +49,13 @@ cc_library_shared { ], export_include_dirs: ["include"], + + product_variables: { + // `debuggable` is set for eng and userdebug builds + debuggable: { + cflags: [ + "-DANDROID_DEBUGGABLE", + ], + }, + }, } diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 4a0a839948..5f5f85a2ad 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -126,7 +126,20 @@ static const std::string getSystemNativeLibraries(NativeLibrary type) { } bool GraphicsEnv::isDebuggable() { - return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0; + // This flag determines if the application is marked debuggable + bool appDebuggable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0; + + // This flag is set only in `debuggable` builds of the platform +#if defined(ANDROID_DEBUGGABLE) + bool platformDebuggable = true; +#else + bool platformDebuggable = false; +#endif + + ALOGV("GraphicsEnv::isDebuggable returning appDebuggable=%s || platformDebuggable=%s", + appDebuggable ? "true" : "false", platformDebuggable ? "true" : "false"); + + return appDebuggable || platformDebuggable; } void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path, diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 82a6b6c2c0..73d3196948 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -35,7 +35,7 @@ public: // Check if the process is debuggable. It returns false except in any of the // following circumstances: - // 1. ro.debuggable=1 (global debuggable enabled). + // 1. ANDROID_DEBUGGABLE is defined (global debuggable enabled). // 2. android:debuggable="true" in the manifest for an individual app. // 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1). // 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of 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/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h index 94b7543e14..b2ca481aa7 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h @@ -82,16 +82,13 @@ public: * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is * the highest quality * @param exif pointer to the exif metadata. - * @param hdr_ratio HDR ratio. If not configured, this value will be calculated by the JPEG/R - * encoder. * @return NO_ERROR if encoding succeeds, error code if error occurs. */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, jr_compressed_ptr dest, int quality, - jr_exif_ptr exif, - float hdr_ratio = 0.0f); + jr_exif_ptr exif); /* * Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG. @@ -104,15 +101,12 @@ public: * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format * @param compressed_jpeg_image compressed 8-bit JPEG image * @param dest destination of the compressed JPEGR image - * @param hdr_ratio HDR ratio. If not configured, this value will be calculated by the JPEG/R - * encoder. * @return NO_ERROR if encoding succeeds, error code if error occurs. */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, jr_compressed_ptr compressed_jpeg_image, - jr_compressed_ptr dest, - float hdr_ratio = 0.0f); + jr_compressed_ptr dest); /* * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV. @@ -125,14 +119,11 @@ public: * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param compressed_jpeg_image compressed 8-bit JPEG image * @param dest destination of the compressed JPEGR image - * @param hdr_ratio HDR ratio. If not configured, this value will be calculated by the JPEG/R - * encoder. * @return NO_ERROR if encoding succeeds, error code if error occurs. */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_compressed_ptr compressed_jpeg_image, - jr_compressed_ptr dest, - float hdr_ratio = 0.0f); + jr_compressed_ptr dest); /* * Decompress JPEGR image. @@ -185,11 +176,13 @@ private: * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param dest recovery map; caller responsible for memory of data + * @param hdr_ratio HDR ratio will be updated in this method * @return NO_ERROR if calculation succeeds, error code if error occurs. */ status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, jr_uncompressed_ptr uncompressed_p010_image, - jr_uncompressed_ptr dest); + jr_uncompressed_ptr dest, + float &hdr_ratio); /* * This method is called in the decoding pipeline. It will take the uncompressed (decoded) @@ -222,11 +215,13 @@ private: * * @param compressed_jpeg_image compressed 8-bit JPEG image * @param compress_recovery_map compressed recover map + * @param hdr_ratio HDR ratio * @param dest compressed JPEGR image * @return NO_ERROR if calculation succeeds, error code if error occurs. */ status_t appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, jr_compressed_ptr compressed_recovery_map, + float hdr_ratio, jr_compressed_ptr dest); /* diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp index 4a90053b12..bd16a68b0d 100644 --- a/libs/jpegrecoverymap/recoverymap.cpp +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -20,11 +20,11 @@ // TODO: handle PQ encode/decode (currently only HLG) #include <jpegrecoverymap/recoverymap.h> - #include <jpegrecoverymap/jpegencoder.h> #include <jpegrecoverymap/jpegdecoder.h> #include <jpegrecoverymap/recoverymapmath.h> +#include <image_io/jpeg/jpeg_marker.h> #include <image_io/xml/xml_writer.h> #include <memory> @@ -60,12 +60,30 @@ string Name(const string &prefix, const string &suffix) { return ss.str(); } +/* + * Helper function used for writing data to destination. + * + * @param destination destination of the data to be written. + * @param source source of data being written. + * @param length length of the data to be written. + * @param position cursor in desitination where the data is to be written. + * @return status of succeed or error code. + */ +status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) { + if (position + length > destination->length) { + return ERROR_JPEGR_BUFFER_TOO_SMALL; + } + + memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length); + position += length; + return NO_ERROR; +} + status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, jr_compressed_ptr dest, int quality, - jr_exif_ptr /* exif */, - float /* hdr_ratio */) { + jr_exif_ptr /* exif */) { if (uncompressed_p010_image == nullptr || uncompressed_yuv_420_image == nullptr || dest == nullptr) { @@ -82,7 +100,9 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, } jpegr_uncompressed_struct map; - JPEGR_CHECK(generateRecoveryMap(uncompressed_yuv_420_image, uncompressed_p010_image, &map)); + float hdr_ratio = 0.0f; + JPEGR_CHECK(generateRecoveryMap( + uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); std::unique_ptr<uint8_t[]> map_data; map_data.reset(reinterpret_cast<uint8_t*>(map.data)); @@ -93,18 +113,17 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); JpegEncoder jpeg_encoder; - // TODO: what quality to use? // TODO: ICC data - need color space information if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data, uncompressed_yuv_420_image->width, - uncompressed_yuv_420_image->height, 95, nullptr, 0)) { + uncompressed_yuv_420_image->height, quality, nullptr, 0)) { return ERROR_JPEGR_ENCODE_ERROR; } jpegr_compressed_struct jpeg; jpeg.data = jpeg_encoder.getCompressedImagePtr(); jpeg.length = jpeg_encoder.getCompressedImageSize(); - JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, dest)); + JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, hdr_ratio, dest)); return NO_ERROR; } @@ -112,8 +131,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, jr_compressed_ptr compressed_jpeg_image, - jr_compressed_ptr dest, - float /* hdr_ratio */) { + jr_compressed_ptr dest) { if (uncompressed_p010_image == nullptr || uncompressed_yuv_420_image == nullptr || compressed_jpeg_image == nullptr @@ -127,7 +145,9 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, } jpegr_uncompressed_struct map; - JPEGR_CHECK(generateRecoveryMap(uncompressed_yuv_420_image, uncompressed_p010_image, &map)); + float hdr_ratio = 0.0f; + JPEGR_CHECK(generateRecoveryMap( + uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); std::unique_ptr<uint8_t[]> map_data; map_data.reset(reinterpret_cast<uint8_t*>(map.data)); @@ -137,15 +157,14 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, compressed_map.data = compressed_map_data.get(); JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); - JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, dest)); + JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest)); return NO_ERROR; } status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_compressed_ptr compressed_jpeg_image, - jr_compressed_ptr dest, - float /* hdr_ratio */) { + jr_compressed_ptr dest) { if (uncompressed_p010_image == nullptr || compressed_jpeg_image == nullptr || dest == nullptr) { @@ -167,7 +186,9 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, } jpegr_uncompressed_struct map; - JPEGR_CHECK(generateRecoveryMap(&uncompressed_yuv_420_image, uncompressed_p010_image, &map)); + float hdr_ratio = 0.0f; + JPEGR_CHECK(generateRecoveryMap( + &uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); std::unique_ptr<uint8_t[]> map_data; map_data.reset(reinterpret_cast<uint8_t*>(map.data)); @@ -177,7 +198,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, compressed_map.data = compressed_map_data.get(); JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); - JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, dest)); + JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest)); return NO_ERROR; } @@ -256,7 +277,8 @@ status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recov status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, jr_uncompressed_ptr uncompressed_p010_image, - jr_uncompressed_ptr dest) { + jr_uncompressed_ptr dest, + float &hdr_ratio) { if (uncompressed_yuv_420_image == nullptr || uncompressed_p010_image == nullptr || dest == nullptr) { @@ -291,7 +313,7 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 } float y_hdr_max_nits = hlgInvOetf(yp_hdr_max); - float hdr_ratio = y_hdr_max_nits / kSdrWhiteNits; + hdr_ratio = y_hdr_max_nits / kSdrWhiteNits; for (size_t y = 0; y < map_height; ++y) { for (size_t x = 0; x < map_width; ++x) { @@ -368,6 +390,7 @@ status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_imag status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, jr_compressed_ptr compressed_recovery_map, + float hdr_ratio, jr_compressed_ptr dest) { if (compressed_jpeg_image == nullptr || compressed_recovery_map == nullptr @@ -375,7 +398,39 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + string xmp = generateXmp(compressed_recovery_map->length, hdr_ratio); + string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; + + // 2 bytes: APP1 sign (ff e1) + // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0" + // x bytes: length of xmp packet + int length = 2 + nameSpace.size() + xmp.size(); + uint8_t lengthH = ((length >> 8) & 0xff); + uint8_t lengthL = (length & 0xff); + + int pos = 0; + + // JPEG/R structure: + // SOI (ff d8) + // APP1 (ff e1) + // 2 bytes of length (2 + 29 + length of xmp packet) + // name space ("http://ns.adobe.com/xap/1.0/\0") + // xmp + // primary image (without the first two bytes, the SOI sign) + // secondary image (the recovery map) + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos)); + JPEGR_CHECK(Write(dest, &lengthH, 1, pos)); + JPEGR_CHECK(Write(dest, &lengthL, 1, pos)); + JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpace.size(), pos)); + JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos)); + JPEGR_CHECK(Write(dest, + (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos)); + JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos)); + dest->length = pos; + return NO_ERROR; } 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 9a6ebaaed2..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}); } @@ -2092,9 +2095,8 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( } bool isSplit = shouldSplitTouch(tempTouchState, entry); - const bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 && - (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source || - tempTouchState.displayId != displayId); + const bool switchedDevice = (oldState != nullptr) && + (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source); const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || @@ -2104,7 +2106,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; - if (switchedDevice && tempTouchState.down && !down && !isHoverAction) { + if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) { ALOGI("Dropping event because a pointer for a different device is already down " "in display %" PRId32, displayId); @@ -2113,10 +2115,8 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( return touchedWindows; // wrong device } tempTouchState.reset(); - tempTouchState.down = down; tempTouchState.deviceId = entry.deviceId; tempTouchState.source = entry.source; - tempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { ALOGI("Dropping move event because a pointer for a different device is already active " @@ -2198,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. @@ -2234,12 +2234,11 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. - if (!tempTouchState.down) { - if (DEBUG_FOCUS) { - ALOGD("Dropping event because the pointer is not down or we previously " - "dropped the pointer down event in display %" PRId32, - displayId); - } + if (!tempTouchState.isDown()) { + 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; } @@ -2279,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. @@ -2287,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; @@ -2320,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 @@ -2333,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)); } } @@ -2346,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()); @@ -2358,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; @@ -2383,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)); } } @@ -2414,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); } } } @@ -2445,11 +2445,9 @@ Failed: if (isHoverAction) { // Started hovering, therefore no longer down. - if (oldState && oldState->down) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Hover received while pointer was " - "down."); - } + if (oldState && oldState->isDown()) { + ALOGD_IF(DEBUG_FOCUS, + "Conflicting pointer actions: Hover received while pointer was down."); *outConflictingPointerActions = true; } tempTouchState.reset(); @@ -2457,7 +2455,6 @@ Failed: maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { tempTouchState.deviceId = entry.deviceId; tempTouchState.source = entry.source; - tempTouchState.displayId = displayId; } } else if (maskedAction == AMOTION_EVENT_ACTION_UP || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { @@ -2465,10 +2462,8 @@ Failed: tempTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. - if (oldState && oldState->down) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Down received while already down."); - } + if (oldState && oldState->isDown()) { + ALOGD("Conflicting pointer actions: Down received while already down."); *outConflictingPointerActions = true; } } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -2501,7 +2496,7 @@ Failed: // Save changes unless the action was scroll in which case the temporary touch // state was only valid for this one action. if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { - if (tempTouchState.displayId >= 0) { + if (displayId >= 0) { mTouchStatesByDisplay[displayId] = tempTouchState; } else { mTouchStatesByDisplay.erase(displayId); @@ -2616,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 = @@ -2664,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()) { @@ -2696,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) { @@ -2925,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()); } @@ -2944,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); @@ -2995,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()) { @@ -3016,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. @@ -3064,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; @@ -3092,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; } @@ -3195,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 } @@ -3255,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++) { @@ -3268,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(); @@ -3670,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(); @@ -3705,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. @@ -3741,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) { @@ -3767,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. @@ -4830,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(); @@ -5104,16 +5101,16 @@ void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { mMaximumObscuringOpacityForTouch = opacity; } -std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked( - const sp<IBinder>& token) { +std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/> +InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) { for (auto& [displayId, state] : mTouchStatesByDisplay) { for (TouchedWindow& w : state.windows) { if (w.windowHandle->getToken() == token) { - return std::make_pair(&state, &w); + return std::make_tuple(&state, &w, displayId); } } } - return std::make_pair(nullptr, nullptr); + return std::make_tuple(nullptr, nullptr, ADISPLAY_ID_DEFAULT); } bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, @@ -5129,13 +5126,12 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< std::scoped_lock _l(mLock); // Find the target touch state and touched window by fromToken. - auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken); + auto [state, touchedWindow, displayId] = findTouchStateWindowAndDisplayLocked(fromToken); if (state == nullptr || touchedWindow == nullptr) { ALOGD("Focus transfer failed because from window is not being touched."); return false; } - const int32_t displayId = state->displayId; sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); if (toWindowHandle == nullptr) { ALOGW("Cannot transfer focus because to window not found."); @@ -5149,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); @@ -5212,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(), @@ -5324,25 +5320,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!mTouchStatesByDisplay.empty()) { dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); - for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) { - const TouchState& state = pair.second; - dump += StringPrintf(INDENT2 "%d: down=%s, deviceId=%d, source=0x%08x\n", - state.displayId, toString(state.down), 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"; - } + for (const auto& [displayId, state] : mTouchStatesByDisplay) { + std::string touchStateDump = addLinePrefix(state.dump(), INDENT2); + dump += INDENT2 + std::to_string(displayId) + " : " + touchStateDump; } } else { dump += INDENT "TouchStates: <no displays touched>\n"; @@ -5687,8 +5667,8 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { return BAD_VALUE; } - auto [statePtr, windowPtr] = findTouchStateAndWindowLocked(token); - if (statePtr == nullptr || windowPtr == nullptr || !statePtr->down) { + auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token); + if (statePtr == nullptr || windowPtr == nullptr || windowPtr->pointerIds.isEmpty()) { ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams." " Ignoring."); return BAD_VALUE; @@ -5700,7 +5680,7 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "input channel stole pointer stream"); options.deviceId = state.deviceId; - options.displayId = state.displayId; + options.displayId = displayId; options.pointerIds = window.pointerIds; std::string canceledWindows; for (const TouchedWindow& w : state.windows) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 3b44a8e85d..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); @@ -676,8 +676,8 @@ private: bool handled) REQUIRES(mLock); // Find touched state and touched window by token. - std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token) - REQUIRES(mLock); + std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/> + findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock); // Statistics gathering. LatencyAggregator mLatencyAggregator GUARDED_BY(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 a3f45cfce3..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)) { @@ -138,4 +141,24 @@ sp<WindowInfoHandle> TouchState::getWallpaperWindow() const { return nullptr; } +bool TouchState::isDown() const { + return std::any_of(windows.begin(), windows.end(), + [](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 f6e9fb69f8..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 { @@ -28,14 +27,10 @@ class WindowInfoHandle; namespace inputdispatcher { struct TouchState { - bool down = false; - // id of the device that is currently down, others are rejected int32_t deviceId = -1; // source of the device that is current down, others are rejected uint32_t source = 0; - // id to the display that currently has a touch, others are rejected - int32_t displayId = ADISPLAY_ID_NONE; std::vector<TouchedWindow> windows; @@ -45,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(); @@ -59,6 +54,9 @@ struct TouchState { sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const; bool isSlippery() const; 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/Android.bp b/services/inputflinger/reader/Android.bp index a53fcd763d..24168a12a6 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -55,6 +55,7 @@ filegroup { "mapper/accumulator/CursorButtonAccumulator.cpp", "mapper/accumulator/CursorScrollAccumulator.cpp", "mapper/accumulator/HidUsageAccumulator.cpp", + "mapper/accumulator/MultiTouchMotionAccumulator.cpp", "mapper/accumulator/SingleTouchMotionAccumulator.cpp", "mapper/accumulator/TouchButtonAccumulator.cpp", ], 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/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index acba4f6513..8e757a5bf7 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -28,163 +28,6 @@ namespace android { // Maximum number of slots supported when using the slot-based Multitouch Protocol B. static constexpr size_t MAX_SLOTS = 32; -// --- MultiTouchMotionAccumulator --- - -MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() - : mCurrentSlot(-1), - mUsingSlotsProtocol(false), - mHaveStylus(false) {} - -void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount, - bool usingSlotsProtocol) { - mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE); - mSlots = std::vector<Slot>(slotCount); - - mCurrentSlot = -1; - if (mUsingSlotsProtocol) { - // Query the driver for the current slot index and use it as the initial slot - // before we start reading events from the device. It is possible that the - // current slot index will not be the same as it was when the first event was - // written into the evdev buffer, which means the input mapper could start - // out of sync with the initial state of the events in the evdev buffer. - // In the extremely unlikely case that this happens, the data from - // two slots will be confused until the next ABS_MT_SLOT event is received. - // This can cause the touch point to "jump", but at least there will be - // no stuck touches. - int32_t initialSlot; - if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); - status == OK) { - mCurrentSlot = initialSlot; - } else { - ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); - } - } -} - -void MultiTouchMotionAccumulator::resetSlots() { - for (Slot& slot : mSlots) { - slot.clear(); - } - mCurrentSlot = -1; -} - -void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - bool newSlot = false; - if (mUsingSlotsProtocol) { - if (rawEvent->code == ABS_MT_SLOT) { - mCurrentSlot = rawEvent->value; - newSlot = true; - } - } else if (mCurrentSlot < 0) { - mCurrentSlot = 0; - } - - if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) { - if (DEBUG_POINTERS) { - if (newSlot) { - ALOGW("MultiTouch device emitted invalid slot index %d but it " - "should be between 0 and %zd; ignoring this slot.", - mCurrentSlot, mSlots.size() - 1); - } - } - } else { - Slot& slot = mSlots[mCurrentSlot]; - // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of - // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while - // updating the slot. - if (!mUsingSlotsProtocol) { - slot.mInUse = true; - } - - switch (rawEvent->code) { - case ABS_MT_POSITION_X: - slot.mAbsMTPositionX = rawEvent->value; - warnIfNotInUse(*rawEvent, slot); - break; - case ABS_MT_POSITION_Y: - slot.mAbsMTPositionY = rawEvent->value; - warnIfNotInUse(*rawEvent, slot); - break; - case ABS_MT_TOUCH_MAJOR: - slot.mAbsMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot.mAbsMTTouchMinor = rawEvent->value; - slot.mHaveAbsMTTouchMinor = true; - break; - case ABS_MT_WIDTH_MAJOR: - slot.mAbsMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot.mAbsMTWidthMinor = rawEvent->value; - slot.mHaveAbsMTWidthMinor = true; - break; - case ABS_MT_ORIENTATION: - slot.mAbsMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - // The slot is no longer in use but it retains its previous contents, - // which may be reused for subsequent touches. - slot.mInUse = false; - } else { - slot.mInUse = true; - slot.mAbsMTTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot.mAbsMTPressure = rawEvent->value; - break; - case ABS_MT_DISTANCE: - slot.mAbsMTDistance = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot.mAbsMTToolType = rawEvent->value; - slot.mHaveAbsMTToolType = true; - break; - } - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - mCurrentSlot += 1; - } -} - -void MultiTouchMotionAccumulator::finishSync() { - if (!mUsingSlotsProtocol) { - resetSlots(); - } -} - -bool MultiTouchMotionAccumulator::hasStylus() const { - return mHaveStylus; -} - -void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) { - if (!slot.mInUse) { - ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i", - event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId); - } -} - -// --- MultiTouchMotionAccumulator::Slot --- - -int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { - if (mHaveAbsMTToolType) { - switch (mAbsMTToolType) { - case MT_TOOL_FINGER: - return AMOTION_EVENT_TOOL_TYPE_FINGER; - case MT_TOOL_PEN: - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - case MT_TOOL_PALM: - return AMOTION_EVENT_TOOL_TYPE_PALM; - } - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - // --- MultiTouchInputMapper --- MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext) diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index 047e62de62..ddf9e80a6c 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -17,77 +17,10 @@ #pragma once #include "TouchInputMapper.h" +#include "accumulator/MultiTouchMotionAccumulator.h" namespace android { -/* Keeps track of the state of multi-touch protocol. */ -class MultiTouchMotionAccumulator { -public: - class Slot { - public: - inline bool isInUse() const { return mInUse; } - inline int32_t getX() const { return mAbsMTPositionX; } - inline int32_t getY() const { return mAbsMTPositionY; } - inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } - inline int32_t getTouchMinor() const { - return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; - } - inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } - inline int32_t getToolMinor() const { - return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; - } - inline int32_t getOrientation() const { return mAbsMTOrientation; } - inline int32_t getTrackingId() const { return mAbsMTTrackingId; } - inline int32_t getPressure() const { return mAbsMTPressure; } - inline int32_t getDistance() const { return mAbsMTDistance; } - inline int32_t getToolType() const; - - private: - friend class MultiTouchMotionAccumulator; - - bool mInUse = false; - bool mHaveAbsMTTouchMinor = false; - bool mHaveAbsMTWidthMinor = false; - bool mHaveAbsMTToolType = false; - - int32_t mAbsMTPositionX = 0; - int32_t mAbsMTPositionY = 0; - int32_t mAbsMTTouchMajor = 0; - int32_t mAbsMTTouchMinor = 0; - int32_t mAbsMTWidthMajor = 0; - int32_t mAbsMTWidthMinor = 0; - int32_t mAbsMTOrientation = 0; - int32_t mAbsMTTrackingId = -1; - int32_t mAbsMTPressure = 0; - int32_t mAbsMTDistance = 0; - int32_t mAbsMTToolType = 0; - - void clear() { *this = Slot(); } - }; - - MultiTouchMotionAccumulator(); - - void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); - void process(const RawEvent* rawEvent); - void finishSync(); - bool hasStylus() const; - - inline size_t getSlotCount() const { return mSlots.size(); } - inline const Slot& getSlot(size_t index) const { - LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); - return mSlots[index]; - } - -private: - int32_t mCurrentSlot; - std::vector<Slot> mSlots; - bool mUsingSlotsProtocol; - bool mHaveStylus; - - void resetSlots(); - void warnIfNotInUse(const RawEvent& event, const Slot& slot); -}; - class MultiTouchInputMapper : public TouchInputMapper { public: explicit MultiTouchInputMapper(InputDeviceContext& deviceContext); 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/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp new file mode 100644 index 0000000000..b0cef676fb --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -0,0 +1,176 @@ +/* + * 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. + */ + +// clang-format off +#include "../Macros.h" +// clang-format on +#include "MultiTouchMotionAccumulator.h" + +namespace android { + +// --- MultiTouchMotionAccumulator --- + +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() + : mCurrentSlot(-1), mUsingSlotsProtocol(false), mHaveStylus(false) {} + +void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount, + bool usingSlotsProtocol) { + mUsingSlotsProtocol = usingSlotsProtocol; + mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE); + mSlots = std::vector<Slot>(slotCount); + + mCurrentSlot = -1; + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot before we + // start reading events from the device. It is possible that the current slot index will + // not be the same as it was when the first event was written into the evdev buffer, which + // means the input mapper could start out of sync with the initial state of the events in + // the evdev buffer. In the extremely unlikely case that this happens, the data from two + // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the + // touch point to "jump", but at least there will be no stuck touches. + int32_t initialSlot; + if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); + status == OK) { + mCurrentSlot = initialSlot; + } else { + ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); + } + } +} + +void MultiTouchMotionAccumulator::resetSlots() { + for (Slot& slot : mSlots) { + slot.clear(); + } + mCurrentSlot = -1; +} + +void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + bool newSlot = false; + if (mUsingSlotsProtocol) { + if (rawEvent->code == ABS_MT_SLOT) { + mCurrentSlot = rawEvent->value; + newSlot = true; + } + } else if (mCurrentSlot < 0) { + mCurrentSlot = 0; + } + + if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) { + if (newSlot) { + ALOGW_IF(DEBUG_POINTERS, + "MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %zd; ignoring this slot.", + mCurrentSlot, mSlots.size() - 1); + } + } else { + Slot& slot = mSlots[mCurrentSlot]; + // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of + // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while + // updating the slot. + if (!mUsingSlotsProtocol) { + slot.mInUse = true; + } + + switch (rawEvent->code) { + case ABS_MT_POSITION_X: + slot.mAbsMtPositionX = rawEvent->value; + warnIfNotInUse(*rawEvent, slot); + break; + case ABS_MT_POSITION_Y: + slot.mAbsMtPositionY = rawEvent->value; + warnIfNotInUse(*rawEvent, slot); + break; + case ABS_MT_TOUCH_MAJOR: + slot.mAbsMtTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + slot.mAbsMtTouchMinor = rawEvent->value; + slot.mHaveAbsMtTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + slot.mAbsMtWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + slot.mAbsMtWidthMinor = rawEvent->value; + slot.mHaveAbsMtWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + slot.mAbsMtOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + if (mUsingSlotsProtocol && rawEvent->value < 0) { + // The slot is no longer in use but it retains its previous contents, + // which may be reused for subsequent touches. + slot.mInUse = false; + } else { + slot.mInUse = true; + slot.mAbsMtTrackingId = rawEvent->value; + } + break; + case ABS_MT_PRESSURE: + slot.mAbsMtPressure = rawEvent->value; + break; + case ABS_MT_DISTANCE: + slot.mAbsMtDistance = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot.mAbsMtToolType = rawEvent->value; + slot.mHaveAbsMtToolType = true; + break; + } + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + mCurrentSlot += 1; + } +} + +void MultiTouchMotionAccumulator::finishSync() { + if (!mUsingSlotsProtocol) { + resetSlots(); + } +} + +bool MultiTouchMotionAccumulator::hasStylus() const { + return mHaveStylus; +} + +void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) { + if (!slot.mInUse) { + ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i", + event.code, event.value, mCurrentSlot, slot.mAbsMtTrackingId); + } +} + +// --- MultiTouchMotionAccumulator::Slot --- + +int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { + if (mHaveAbsMtToolType) { + switch (mAbsMtToolType) { + case MT_TOOL_FINGER: + return AMOTION_EVENT_TOOL_TYPE_FINGER; + case MT_TOOL_PEN: + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + case MT_TOOL_PALM: + return AMOTION_EVENT_TOOL_TYPE_PALM; + } + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h new file mode 100644 index 0000000000..625a00fdd4 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <linux/input-event-codes.h> +#include <stdint.h> +#include <vector> + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +/* Keeps track of the state of multi-touch protocol. */ +class MultiTouchMotionAccumulator { +public: + class Slot { + public: + inline bool isInUse() const { return mInUse; } + inline int32_t getX() const { return mAbsMtPositionX; } + inline int32_t getY() const { return mAbsMtPositionY; } + inline int32_t getTouchMajor() const { return mAbsMtTouchMajor; } + inline int32_t getTouchMinor() const { + return mHaveAbsMtTouchMinor ? mAbsMtTouchMinor : mAbsMtTouchMajor; + } + inline int32_t getToolMajor() const { return mAbsMtWidthMajor; } + inline int32_t getToolMinor() const { + return mHaveAbsMtWidthMinor ? mAbsMtWidthMinor : mAbsMtWidthMajor; + } + inline int32_t getOrientation() const { return mAbsMtOrientation; } + inline int32_t getTrackingId() const { return mAbsMtTrackingId; } + inline int32_t getPressure() const { return mAbsMtPressure; } + inline int32_t getDistance() const { return mAbsMtDistance; } + int32_t getToolType() const; + + private: + friend class MultiTouchMotionAccumulator; + + bool mInUse = false; + bool mHaveAbsMtTouchMinor = false; + bool mHaveAbsMtWidthMinor = false; + bool mHaveAbsMtToolType = false; + + int32_t mAbsMtPositionX = 0; + int32_t mAbsMtPositionY = 0; + int32_t mAbsMtTouchMajor = 0; + int32_t mAbsMtTouchMinor = 0; + int32_t mAbsMtWidthMajor = 0; + int32_t mAbsMtWidthMinor = 0; + int32_t mAbsMtOrientation = 0; + int32_t mAbsMtTrackingId = -1; + int32_t mAbsMtPressure = 0; + int32_t mAbsMtDistance = 0; + int32_t mAbsMtToolType = 0; + + void clear() { *this = Slot(); } + }; + + MultiTouchMotionAccumulator(); + + void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); + void process(const RawEvent* rawEvent); + void finishSync(); + bool hasStylus() const; + + inline size_t getSlotCount() const { return mSlots.size(); } + inline const Slot& getSlot(size_t index) const { + LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); + return mSlots[index]; + } + +private: + int32_t mCurrentSlot; + std::vector<Slot> mSlots; + bool mUsingSlotsProtocol; + bool mHaveStylus; + + void resetSlots(); + void warnIfNotInUse(const RawEvent& event, const Slot& slot); +}; + +} // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index ecc42ba5c2..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); @@ -2373,7 +2379,7 @@ TEST_F(InputReaderIntegrationTest, TestInvalidDevice) { // An invalid input device that is only used for this test. class InvalidUinputDevice : public UinputDevice { public: - InvalidUinputDevice() : UinputDevice("Invalid Device") {} + InvalidUinputDevice() : UinputDevice("Invalid Device", 99 /*productId*/) {} private: void configureDevice(int fd, uinput_user_dev* device) override {} @@ -2825,6 +2831,96 @@ TEST_F(TouchIntegrationTest, StylusButtonsGenerateKeyEvents) { WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); } +TEST_F(TouchIntegrationTest, StylusButtonsSurroundingTouchGesture) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Press the stylus button. + mDevice->sendKey(BTN_STYLUS, 1); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + + // Start and finish a stylus gesture. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_PEN); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + mDevice->sendTrackingId(INVALID_TRACKING_ID); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + + // Release the stylus button. + mDevice->sendKey(BTN_STYLUS, 0); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); +} + +TEST_F(TouchIntegrationTest, StylusButtonsWithinTouchGesture) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Start a stylus gesture. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_PEN); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + + // Press and release a stylus button. Each change in button state also generates a MOVE event. + mDevice->sendKey(BTN_STYLUS, 1); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + mDevice->sendKey(BTN_STYLUS, 0); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + + // Finish the stylus gesture. + mDevice->sendTrackingId(INVALID_TRACKING_ID); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); +} + // --- InputDeviceTest --- class InputDeviceTest : public testing::Test { protected: @@ -3165,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(); } @@ -3209,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; } @@ -5396,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 { @@ -7389,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) { @@ -7502,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) { @@ -10138,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/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index c4830dc815..bc695b8bd8 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -24,7 +24,8 @@ namespace android { // --- UinputDevice --- -UinputDevice::UinputDevice(const char* name) : mName(name) {} +UinputDevice::UinputDevice(const char* name, int16_t productId) + : mName(name), mProductId(productId) {} UinputDevice::~UinputDevice() { if (ioctl(mDeviceFd, UI_DEV_DESTROY)) { @@ -43,7 +44,7 @@ void UinputDevice::init() { strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE); device.id.bustype = BUS_USB; device.id.vendor = 0x01; - device.id.product = 0x01; + device.id.product = mProductId; device.id.version = 1; ASSERT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device)); @@ -76,8 +77,8 @@ void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) { // --- UinputKeyboard --- -UinputKeyboard::UinputKeyboard(const char* name, std::initializer_list<int> keys) - : UinputDevice(name), mKeys(keys.begin(), keys.end()) {} +UinputKeyboard::UinputKeyboard(const char* name, int16_t productId, std::initializer_list<int> keys) + : UinputDevice(name, productId), mKeys(keys.begin(), keys.end()) {} void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) { // enable key press/release event @@ -121,23 +122,26 @@ void UinputKeyboard::pressAndReleaseKey(int key) { // --- UinputHomeKey --- -UinputHomeKey::UinputHomeKey() : UinputKeyboard("Test Uinput Home Key", {KEY_HOME}) {} +UinputHomeKey::UinputHomeKey() : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {KEY_HOME}) {} void UinputHomeKey::pressAndReleaseHomeKey() { pressAndReleaseKey(KEY_HOME); } -// --- UinputSteamController +// --- UinputSteamController --- + UinputSteamController::UinputSteamController() - : UinputKeyboard("Test Uinput Steam Controller", {BTN_GEAR_DOWN, BTN_GEAR_UP}) {} + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_GEAR_DOWN, BTN_GEAR_UP}) {} + +// --- UinputExternalStylus --- -// --- UinputExternalStylus UinputExternalStylus::UinputExternalStylus() - : UinputKeyboard("Test Uinput External Stylus", {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {} + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {} // --- UinputTouchScreen --- -UinputTouchScreen::UinputTouchScreen(const Rect* size) - : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {} + +UinputTouchScreen::UinputTouchScreen(const Rect& size) + : UinputDevice(DEVICE_NAME, PRODUCT_ID), mSize(size) {} void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { // Setup the touch screen device diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index 53dcfd0a68..d661bd36d3 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -32,7 +32,7 @@ namespace android { template <class D, class... Ts> std::unique_ptr<D> createUinputDevice(Ts... args) { // Using `new` to access non-public constructors. - std::unique_ptr<D> dev(new D(&args...)); + std::unique_ptr<D> dev(new D(args...)); EXPECT_NO_FATAL_FAILURE(dev->init()); return dev; } @@ -51,8 +51,9 @@ public: protected: const char* mName; + const int16_t mProductId; - UinputDevice(const char* name); + explicit UinputDevice(const char* name, int16_t productId); // Signals which types of events this device supports before it is created. // This must be overridden by subclasses. @@ -71,7 +72,8 @@ private: class UinputKeyboard : public UinputDevice { public: - static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device"; + static constexpr const char* KEYBOARD_NAME = "Test Uinput Keyboard Device"; + static constexpr int16_t PRODUCT_ID = 42; // Injects key press and sync. void pressKey(int key); @@ -84,7 +86,8 @@ public: friend std::unique_ptr<D> createUinputDevice(Ts... args); protected: - UinputKeyboard(const char* name, std::initializer_list<int> keys = {}); + explicit UinputKeyboard(const char* name, int16_t productId = PRODUCT_ID, + std::initializer_list<int> keys = {}); private: void configureDevice(int fd, uinput_user_dev* device) override; @@ -97,6 +100,9 @@ private: // A keyboard device that has a single HOME key. class UinputHomeKey : public UinputKeyboard { public: + static constexpr const char* DEVICE_NAME = "Test Uinput Home Key"; + static constexpr int16_t PRODUCT_ID = 43; + // Injects 4 events: key press, sync, key release, and sync. void pressAndReleaseHomeKey(); @@ -104,34 +110,47 @@ public: friend std::unique_ptr<D> createUinputDevice(Ts... args); private: - UinputHomeKey(); + explicit UinputHomeKey(); }; +// --- UinputSteamController --- + // A joystick device that sends a BTN_GEAR_DOWN / BTN_WHEEL key. class UinputSteamController : public UinputKeyboard { public: + static constexpr const char* DEVICE_NAME = "Test Uinput Steam Controller"; + static constexpr int16_t PRODUCT_ID = 44; + template <class D, class... Ts> friend std::unique_ptr<D> createUinputDevice(Ts... args); private: - UinputSteamController(); + explicit UinputSteamController(); }; +// --- UinputExternalStylus --- + // A stylus that reports button presses. class UinputExternalStylus : public UinputKeyboard { public: + static constexpr const char* DEVICE_NAME = "Test Uinput External Stylus"; + static constexpr int16_t PRODUCT_ID = 45; + template <class D, class... Ts> friend std::unique_ptr<D> createUinputDevice(Ts... args); private: - UinputExternalStylus(); + explicit UinputExternalStylus(); }; // --- UinputTouchScreen --- -// A touch screen device with specific size. + +// A multi-touch touchscreen device with specific size that also supports styluses. class UinputTouchScreen : public UinputDevice { public: - static constexpr const char* DEVICE_NAME = "Test Touch Screen"; + static constexpr const char* DEVICE_NAME = "Test Uinput Touch Screen"; + static constexpr int16_t PRODUCT_ID = 46; + static const int32_t RAW_TOUCH_MIN = 0; static const int32_t RAW_TOUCH_MAX = 31; static const int32_t RAW_ID_MIN = 0; @@ -157,7 +176,7 @@ public: const Point getCenterPoint(); protected: - UinputTouchScreen(const Rect* size); + explicit UinputTouchScreen(const Rect& size); private: void configureDevice(int fd, uinput_user_dev* device) override; diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index b7de61982e..7fb33e5dcf 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -40,7 +40,7 @@ cc_library_shared { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V3-cpp", + "android.hardware.power-V4-cpp", ], cflags: [ diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp index 0286a8160c..4343aec227 100644 --- a/services/powermanager/benchmarks/Android.bp +++ b/services/powermanager/benchmarks/Android.bp @@ -40,7 +40,7 @@ cc_benchmark { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V3-cpp", + "android.hardware.power-V4-cpp", ], static_libs: [ "libtestUtil", diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp index eec6801b1f..54dffcf817 100644 --- a/services/powermanager/tests/Android.bp +++ b/services/powermanager/tests/Android.bp @@ -51,7 +51,7 @@ cc_test { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V3-cpp", + "android.hardware.power-V4-cpp", ], static_libs: [ "libgmock", diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 999c03f96b..14fdd126dd 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -189,6 +189,7 @@ filegroup { "Scheduler/VsyncConfiguration.cpp", "Scheduler/VsyncModulator.cpp", "Scheduler/VsyncSchedule.cpp", + "ScreenCaptureOutput.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceFlingerDefaultFactory.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 23d5570096..e06da33025 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -134,9 +134,11 @@ protected: void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{}; bool getSkipColorTransform() const override; compositionengine::Output::FrameFences presentAndGetFrameFences() override; + virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings() const; std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<LayerFE*> &outLayerFEs) override; + virtual bool layerNeedsFiltering(const OutputLayer*) const; void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override; void setExpensiveRenderingExpected(bool enabled) override; void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index c2b1f0679c..d1daca6090 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -1226,40 +1226,8 @@ std::optional<base::unique_fd> Output::composeSurfaces( ALOGV("hasClientComposition"); - renderengine::DisplaySettings clientCompositionDisplay; - clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent(); - clientCompositionDisplay.clip = outputState.layerStackSpace.getContent(); - clientCompositionDisplay.orientation = - ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation()); - clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut() - ? outputState.dataspace - : ui::Dataspace::UNKNOWN; - - // If we have a valid current display brightness use that, otherwise fall back to the - // display's max desired - clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f - ? outputState.displayBrightnessNits - : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); - clientCompositionDisplay.maxLuminance = - mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); - clientCompositionDisplay.targetLuminanceNits = - outputState.clientTargetBrightness * outputState.displayBrightnessNits; - clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage; - clientCompositionDisplay.renderIntent = - static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>( - outputState.renderIntent); - - // Compute the global color transform matrix. - clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; - for (auto& info : outputState.borderInfoList) { - renderengine::BorderRenderInfo borderInfo; - borderInfo.width = info.width; - borderInfo.color = info.color; - borderInfo.combinedRegion = info.combinedRegion; - clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo)); - } - clientCompositionDisplay.deviceHandlesColorTransform = - outputState.usesDeviceComposition || getSkipColorTransform(); + renderengine::DisplaySettings clientCompositionDisplay = + generateClientCompositionDisplaySettings(); // Generate the client composition requests for the layers on this output. auto& renderEngine = getCompositionEngine().getRenderEngine(); @@ -1350,6 +1318,46 @@ std::optional<base::unique_fd> Output::composeSurfaces( return base::unique_fd(fence->dup()); } +renderengine::DisplaySettings Output::generateClientCompositionDisplaySettings() const { + const auto& outputState = getState(); + + renderengine::DisplaySettings clientCompositionDisplay; + clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent(); + clientCompositionDisplay.clip = outputState.layerStackSpace.getContent(); + clientCompositionDisplay.orientation = + ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation()); + clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut() + ? outputState.dataspace + : ui::Dataspace::UNKNOWN; + + // If we have a valid current display brightness use that, otherwise fall back to the + // display's max desired + clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f + ? outputState.displayBrightnessNits + : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); + clientCompositionDisplay.maxLuminance = + mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); + clientCompositionDisplay.targetLuminanceNits = + outputState.clientTargetBrightness * outputState.displayBrightnessNits; + clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage; + clientCompositionDisplay.renderIntent = + static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>( + outputState.renderIntent); + + // Compute the global color transform matrix. + clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; + for (auto& info : outputState.borderInfoList) { + renderengine::BorderRenderInfo borderInfo; + borderInfo.width = info.width; + borderInfo.color = info.color; + borderInfo.combinedRegion = info.combinedRegion; + clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo)); + } + clientCompositionDisplay.deviceHandlesColorTransform = + outputState.usesDeviceComposition || getSkipColorTransform(); + return clientCompositionDisplay; +} + std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<LayerFE*>& outLayerFEs) { std::vector<LayerFE::LayerSettings> clientCompositionLayers; @@ -1415,7 +1423,7 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( Enabled); compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{.clip = clip, - .needsFiltering = layer->needsFiltering() || + .needsFiltering = layerNeedsFiltering(layer) || outputState.needsFiltering, .isSecure = outputState.isSecure, .supportsProtectedContent = supportsProtectedContent, @@ -1446,6 +1454,10 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( return clientCompositionLayers; } +bool Output::layerNeedsFiltering(const compositionengine::OutputLayer* layer) const { + return layer->needsFiltering(); +} + void Output::appendRegionFlashRequests( const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) { if (flashRegion.isEmpty()) { diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 18ddfbca6c..9868c8ead8 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -313,26 +313,10 @@ ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() { return sPrimaryDisplayRotationFlags; } -std::string DisplayDevice::getDebugName() const { - using namespace std::string_literals; - - std::string name = "Display "s + to_string(getId()) + " ("s; - - name += isVirtual() ? "virtual"s : "physical"s; - - if (isPrimary()) { - name += ", primary"s; - } - - return name + ", \""s + mDisplayName + "\")"s; -} - void DisplayDevice::dump(utils::Dumper& dumper) const { using namespace std::string_view_literals; - dumper.dump({}, getDebugName()); - - utils::Dumper::Indent indent(dumper); + dumper.dump("name"sv, '"' + mDisplayName + '"'); dumper.dump("powerMode"sv, mPowerMode); if (mRefreshRateSelector) { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 6c848bb100..1602a71709 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -248,10 +248,6 @@ public: // release HWC resources (if any) for removable displays void disconnect(); - /* ------------------------------------------------------------------------ - * Debugging - */ - std::string getDebugName() const; void dump(utils::Dumper&) const; private: diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h index 33a7bca038..82388289ed 100644 --- a/services/surfaceflinger/DisplayHardware/Hal.h +++ b/services/surfaceflinger/DisplayHardware/Hal.h @@ -178,7 +178,8 @@ inline std::string to_string(hardware::graphics::composer::hal::Error error) { } // For utils::Dumper ADL. -namespace hardware::graphics::composer::V2_2 { +namespace hardware::graphics::composer { +namespace V2_2 { inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) { switch (mode) { @@ -197,7 +198,9 @@ inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) } } -} // namespace hardware::graphics::composer::V2_2 +} // namespace V2_2 + +namespace V2_1 { inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) { switch (vsync) { @@ -210,4 +213,6 @@ inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) { } } +} // namespace V2_1 +} // namespace hardware::graphics::composer } // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index d1e61a3273..2a18521b5b 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -3499,6 +3499,12 @@ const compositionengine::LayerFECompositionState* Layer::getCompositionState() c return mSnapshot.get(); } +sp<LayerFE> Layer::copyCompositionEngineLayerFE() const { + auto result = mFlinger->getFactory().createLayerFE(mLayerFE->getDebugName()); + result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot); + return result; +} + void Layer::useSurfaceDamage() { if (mFlinger->mForceFullDamage) { surfaceDamageRegion = Region::INVALID_REGION; @@ -3965,14 +3971,17 @@ void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMet } LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) { - if (mLayer) { - mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot); - } + LOG_ALWAYS_FATAL_IF(!mLayer, "LayerSnapshotGuard received a null layer."); + mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot); + LOG_ALWAYS_FATAL_IF(!mLayer->mLayerFE->mSnapshot, + "LayerFE snapshot null after taking ownership from layer"); } LayerSnapshotGuard::~LayerSnapshotGuard() { if (mLayer) { mLayer->mSnapshot = std::move(mLayer->mLayerFE->mSnapshot); + LOG_ALWAYS_FATAL_IF(!mLayer->mSnapshot, + "Layer snapshot null after taking ownership from LayerFE"); } } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index a3c4e5966c..9585fa9b30 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -323,6 +323,7 @@ public: ui::Dataspace getRequestedDataSpace() const; virtual sp<LayerFE> getCompositionEngineLayerFE() const; + virtual sp<LayerFE> copyCompositionEngineLayerFE() const; const LayerSnapshot* getLayerSnapshot() const; LayerSnapshot* editLayerSnapshot(); diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index a6cd47b253..1acf15a55c 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -668,6 +668,7 @@ void EventThread::dump(std::string& result) const { StringAppendF(&result, " %s\n", toString(*connection).c_str()); } } + result += '\n'; } const char* EventThread::toCString(State state) { diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp index c233455994..cb9bfe93db 100644 --- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp +++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp @@ -54,10 +54,9 @@ std::optional<Fps> FrameRateOverrideMappings::getFrameRateOverrideForUid( std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides( bool supportsFrameRateOverrideByContent) { std::lock_guard lock(mFrameRateOverridesLock); + std::vector<FrameRateOverride> overrides; - overrides.reserve(std::max({mFrameRateOverridesFromGameManager.size(), - mFrameRateOverridesFromBackdoor.size(), - mFrameRateOverridesByContent.size()})); + overrides.reserve(maxOverridesCount()); for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) { overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()}); @@ -83,28 +82,34 @@ std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrid return overrides; } -void FrameRateOverrideMappings::dump(std::string& result) const { - using base::StringAppendF; +void FrameRateOverrideMappings::dump(utils::Dumper& dumper) const { + using namespace std::string_view_literals; std::lock_guard lock(mFrameRateOverridesLock); - StringAppendF(&result, "Frame Rate Overrides (backdoor): {"); - for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) { - StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str()); - } - StringAppendF(&result, "}\n"); + const bool hasOverrides = maxOverridesCount() > 0; + dumper.dump("FrameRateOverrides"sv, hasOverrides ? ""sv : "none"sv); - StringAppendF(&result, "Frame Rate Overrides (GameManager): {"); - for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) { - StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str()); - } - StringAppendF(&result, "}\n"); + if (!hasOverrides) return; - StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {"); - for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) { - StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str()); + dump(dumper, "setFrameRate"sv, mFrameRateOverridesByContent); + dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager); + dump(dumper, "Backdoor"sv, mFrameRateOverridesFromBackdoor); +} + +void FrameRateOverrideMappings::dump(utils::Dumper& dumper, std::string_view name, + const UidToFrameRateOverride& overrides) const { + if (overrides.empty()) return; + + utils::Dumper::Indent indent(dumper); + dumper.dump(name); + { + utils::Dumper::Indent indent(dumper); + for (const auto& [uid, frameRate] : overrides) { + using namespace std::string_view_literals; + dumper.dump("(uid, frameRate)"sv, uid, frameRate); + } } - StringAppendF(&result, "}\n"); } bool FrameRateOverrideMappings::updateFrameRateOverridesByContent( diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h index 4185a4c0ab..da0f276a3b 100644 --- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h +++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h @@ -23,7 +23,10 @@ #include <map> #include <optional> +#include "Utils/Dumper.h" + namespace android::scheduler { + class FrameRateOverrideMappings { using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; using UidToFrameRateOverride = std::map<uid_t, Fps>; @@ -34,7 +37,6 @@ public: EXCLUDES(mFrameRateOverridesLock); std::vector<FrameRateOverride> getAllFrameRateOverrides(bool supportsFrameRateOverrideByContent) EXCLUDES(mFrameRateOverridesLock); - void dump(std::string& result) const; bool updateFrameRateOverridesByContent(const UidToFrameRateOverride& frameRateOverrides) EXCLUDES(mFrameRateOverridesLock); void setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride) @@ -42,7 +44,17 @@ public: void setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) EXCLUDES(mFrameRateOverridesLock); + void dump(utils::Dumper&) const; + private: + size_t maxOverridesCount() const REQUIRES(mFrameRateOverridesLock) { + return std::max({mFrameRateOverridesByContent.size(), + mFrameRateOverridesFromGameManager.size(), + mFrameRateOverridesFromBackdoor.size()}); + } + + void dump(utils::Dumper&, std::string_view name, const UidToFrameRateOverride&) const; + // The frame rate override lists need their own mutex as they are being read // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks mutable std::mutex mFrameRateOverridesLock; @@ -53,4 +65,5 @@ private: UidToFrameRateOverride mFrameRateOverridesFromBackdoor GUARDED_BY(mFrameRateOverridesLock); UidToFrameRateOverride mFrameRateOverridesFromGameManager GUARDED_BY(mFrameRateOverridesLock); }; + } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index b884dc873d..7c9cedfab8 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -275,7 +275,7 @@ void LayerHistory::clear() { std::string LayerHistory::dump() const { std::lock_guard lock(mLock); - return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", + return base::StringPrintf("{size=%zu, active=%zu}", mActiveLayerInfos.size() + mInactiveLayerInfos.size(), mActiveLayerInfos.size()); } diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp index 3c8dc64f10..cd45bfdab3 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp +++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp @@ -179,11 +179,5 @@ void OneShotTimer::reset() { } } -std::string OneShotTimer::dump() const { - std::ostringstream stream; - stream << mInterval.count() << " ms"; - return stream.str(); -} - } // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h index 2017c31513..f95646c48f 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.h +++ b/services/surfaceflinger/Scheduler/OneShotTimer.h @@ -23,6 +23,7 @@ #include "../Clock.h" #include <android-base/thread_annotations.h> +#include <scheduler/Time.h> namespace android { namespace scheduler { @@ -42,6 +43,8 @@ public: std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>()); ~OneShotTimer(); + Duration interval() const { return mInterval; } + // Initializes and turns on the idle timer. void start(); // Stops the idle timer and any held resources. @@ -49,8 +52,6 @@ public: // Resets the wakeup time and fires the reset callback. void reset(); - std::string dump() const; - private: // Enum to track in what state is the timer. enum class TimerState { diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index c913891b62..8d4ea0520a 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -1157,20 +1157,15 @@ void RefreshRateSelector::dump(utils::Dumper& dumper) const { dumper.dump("frameRateOverrideConfig"sv, *ftl::enum_name(mFrameRateOverrideConfig)); - std::string idleTimer; - if (mIdleTimer) { - idleTimer = mIdleTimer->dump(); - } else { - idleTimer = "off"sv; - } - - if (const auto controller = mConfig.kernelIdleTimerController) { - base::StringAppendF(&idleTimer, " (kernel via %s)", ftl::enum_string(*controller).c_str()); - } else { - idleTimer += " (platform)"sv; + dumper.dump("idleTimer"sv); + { + utils::Dumper::Indent indent(dumper); + dumper.dump("interval"sv, mIdleTimer.transform(&OneShotTimer::interval)); + dumper.dump("controller"sv, + mConfig.kernelIdleTimerController + .and_then(&ftl::enum_name<KernelIdleTimerController>) + .value_or("Platform"sv)); } - - dumper.dump("idleTimer"sv, idleTimer); } std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() { diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index abbd30465a..0e80817521 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -18,12 +18,12 @@ #include <algorithm> #include <numeric> -#include <optional> #include <type_traits> #include <utility> #include <variant> #include <ftl/concat.h> +#include <ftl/optional.h> #include <gui/DisplayEventReceiver.h> #include <scheduler/Fps.h> @@ -276,7 +276,7 @@ public: // The controller representing how the kernel idle timer will be configured // either on the HWC api or sysprop. - std::optional<KernelIdleTimerController> kernelIdleTimerController; + ftl::Optional<KernelIdleTimerController> kernelIdleTimerController; }; RefreshRateSelector( @@ -474,7 +474,7 @@ private: std::mutex mIdleTimerCallbacksMutex; std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex); // Used to detect (lack of) frame activity. - std::optional<scheduler::OneShotTimer> mIdleTimer; + ftl::Optional<scheduler::OneShotTimer> mIdleTimer; }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 0e1b775a8c..6108d92a50 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -25,6 +25,7 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> +#include <ftl/enum.h> #include <ftl/fake_guard.h> #include <ftl/small_map.h> #include <gui/WindowInfo.h> @@ -94,36 +95,24 @@ void Scheduler::startTimers() { } } -void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) { - // The current RefreshRateSelector instance may outlive this call, so unbind its idle timer. - { - // mRefreshRateSelectorLock is not locked here to avoid the deadlock - // as the callback can attempt to acquire the lock before stopIdleTimer can finish - // the execution. It's safe to FakeGuard as main thread is the only thread that - // writes to the mRefreshRateSelector. - ftl::FakeGuard guard(mRefreshRateSelectorLock); - if (mRefreshRateSelector) { - mRefreshRateSelector->stopIdleTimer(); - mRefreshRateSelector->clearIdleTimerCallbacks(); - } +void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr newSelectorPtr) { + // No need to lock for reads on kMainThreadContext. + if (const auto& selectorPtr = FTL_FAKE_GUARD(mRefreshRateSelectorLock, mRefreshRateSelector)) { + unbindIdleTimer(*selectorPtr); } + { - // Clear state that depends on the current instance. + // Clear state that depends on the current RefreshRateSelector. std::scoped_lock lock(mPolicyLock); mPolicy = {}; } std::scoped_lock lock(mRefreshRateSelectorLock); - mRefreshRateSelector = std::move(selectorPtr); - if (!mRefreshRateSelector) return; - - mRefreshRateSelector->setIdleTimerCallbacks( - {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); }, - .onExpired = [this] { idleTimerCallback(TimerState::Expired); }}, - .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); }, - .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}}); + mRefreshRateSelector = std::move(newSelectorPtr); - mRefreshRateSelector->startIdleTimer(); + if (mRefreshRateSelector) { + bindIdleTimer(*mRefreshRateSelector); + } } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { @@ -546,6 +535,21 @@ void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) { mLayerHistory.clear(); } +void Scheduler::bindIdleTimer(RefreshRateSelector& selector) { + selector.setIdleTimerCallbacks( + {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); }, + .onExpired = [this] { idleTimerCallback(TimerState::Expired); }}, + .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); }, + .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}}); + + selector.startIdleTimer(); +} + +void Scheduler::unbindIdleTimer(RefreshRateSelector& selector) { + selector.stopIdleTimer(); + selector.clearIdleTimerCallbacks(); +} + void Scheduler::kernelIdleTimerCallback(TimerState state) { ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state)); @@ -596,22 +600,36 @@ void Scheduler::displayPowerTimerCallback(TimerState state) { ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); } -void Scheduler::dump(std::string& result) const { - using base::StringAppendF; +void Scheduler::dump(utils::Dumper& dumper) const { + using namespace std::string_view_literals; - StringAppendF(&result, "+ Touch timer: %s\n", - mTouchTimer ? mTouchTimer->dump().c_str() : "off"); - StringAppendF(&result, "+ Content detection: %s %s\n\n", - mFeatures.test(Feature::kContentDetection) ? "on" : "off", - mLayerHistory.dump().c_str()); + { + utils::Dumper::Section section(dumper, "Features"sv); - mFrameRateOverrideMappings.dump(result); + for (Feature feature : ftl::enum_range<Feature>()) { + if (const auto flagOpt = ftl::flag_name(feature)) { + dumper.dump(flagOpt->substr(1), mFeatures.test(feature)); + } + } + } + { + utils::Dumper::Section section(dumper, "Policy"sv); + + dumper.dump("layerHistory"sv, mLayerHistory.dump()); + dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval)); + dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval)); + } + + mFrameRateOverrideMappings.dump(dumper); + dumper.eol(); { + utils::Dumper::Section section(dumper, "Hardware VSYNC"sv); + std::lock_guard lock(mHWVsyncLock); - StringAppendF(&result, - "mScreenAcquired=%d mPrimaryHWVsyncEnabled=%d mHWVsyncAvailable=%d\n", - mScreenAcquired.load(), mPrimaryHWVsyncEnabled, mHWVsyncAvailable); + dumper.dump("screenAcquired"sv, mScreenAcquired.load()); + dumper.dump("hwVsyncAvailable"sv, mHWVsyncAvailable); + dumper.dump("hwVsyncEnabled"sv, mPrimaryHWVsyncEnabled); } } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 901cf74558..04f3b69b98 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -45,6 +45,7 @@ #include "MessageQueue.h" #include "OneShotTimer.h" #include "RefreshRateSelector.h" +#include "Utils/Dumper.h" #include "VsyncSchedule.h" namespace android::scheduler { @@ -108,7 +109,8 @@ public: void startTimers(); using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>; - void setRefreshRateSelector(RefreshRateSelectorPtr) EXCLUDES(mRefreshRateSelectorLock); + void setRefreshRateSelector(RefreshRateSelectorPtr) REQUIRES(kMainThreadContext) + EXCLUDES(mRefreshRateSelectorLock); void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr); void unregisterDisplay(PhysicalDisplayId); @@ -196,7 +198,7 @@ public: // for a given uid bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const; - void dump(std::string&) const; + void dump(utils::Dumper&) const; void dump(ConnectionHandle, std::string&) const; void dumpVsync(std::string&) const; @@ -253,6 +255,13 @@ private: sp<EventThreadConnection> createConnectionInternal( EventThread*, EventRegistrationFlags eventRegistration = {}); + void bindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext, mRefreshRateSelectorLock); + + // Blocks until the timer thread exits. `mRefreshRateSelectorLock` must not be locked by the + // caller on the main thread to avoid deadlock, since the timer thread locks it before exit. + static void unbindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext) + EXCLUDES(mRefreshRateSelectorLock); + // Update feature state machine to given state when corresponding timer resets or expires. void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateSelectorLock); void idleTimerCallback(TimerState); @@ -327,9 +336,9 @@ private: LayerHistory mLayerHistory; // Timer used to monitor touch events. - std::optional<OneShotTimer> mTouchTimer; + ftl::Optional<OneShotTimer> mTouchTimer; // Timer used to monitor display power mode. - std::optional<OneShotTimer> mDisplayPowerTimer; + ftl::Optional<OneShotTimer> mDisplayPowerTimer; ISchedulerCallback& mSchedulerCallback; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h index 2ca55d41ee..bd4e3c27b3 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Time.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h @@ -17,7 +17,9 @@ #pragma once #include <chrono> +#include <string> +#include <android-base/stringprintf.h> #include <utils/Timers.h> namespace android { @@ -79,4 +81,8 @@ constexpr Rep ticks(Duration d) { return std::chrono::duration_cast<D>(d).count(); } +inline std::string to_string(Duration d) { + return base::StringPrintf("%.3f ms", ticks<std::milli, float>(d)); +} + } // namespace android diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp new file mode 100644 index 0000000000..8f93ba40ee --- /dev/null +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -0,0 +1,107 @@ +/* + * 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 "ScreenCaptureOutput.h" +#include "ScreenCaptureRenderSurface.h" + +#include <compositionengine/CompositionEngine.h> +#include <compositionengine/DisplayColorProfileCreationArgs.h> +#include <compositionengine/impl/DisplayColorProfile.h> +#include <ui/Rotation.h> + +namespace android { + +std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs&& args) { + std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated< + ScreenCaptureOutput, compositionengine::CompositionEngine, + ScreenCaptureOutputArgs&&>(args.compositionEngine, std::move(args)); + output->editState().isSecure = args.renderArea.isSecure(); + output->setCompositionEnabled(true); + output->setLayerFilter({args.layerStack}); + output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer))); + output->setDisplayBrightness(args.sdrWhitePointNits, args.displayBrightnessNits); + + output->setDisplayColorProfile(std::make_unique<compositionengine::impl::DisplayColorProfile>( + compositionengine::DisplayColorProfileCreationArgsBuilder() + .setHasWideColorGamut(true) + .Build())); + + ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags()); + Rect orientedDisplaySpaceRect{args.renderArea.getReqWidth(), args.renderArea.getReqHeight()}; + output->setProjection(orientation, args.renderArea.getLayerStackSpaceRect(), + orientedDisplaySpaceRect); + + Rect sourceCrop = args.renderArea.getSourceCrop(); + output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()}); + + return output; +} + +ScreenCaptureOutput::ScreenCaptureOutput(ScreenCaptureOutputArgs&& args) + : mRenderArea(args.renderArea), + mFilterForScreenshot(std::move(args.filterForScreenshot)), + mColorProfile(args.colorProfile), + mRegionSampling(args.regionSampling) {} + +void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) { + auto& outputState = editState(); + outputState.dataspace = mColorProfile.dataspace; + outputState.renderIntent = mColorProfile.renderIntent; +} + +renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisplaySettings() + const { + auto clientCompositionDisplay = + compositionengine::impl::Output::generateClientCompositionDisplaySettings(); + clientCompositionDisplay.clip = mRenderArea.getSourceCrop(); + clientCompositionDisplay.targetLuminanceNits = -1; + return clientCompositionDisplay; +} + +std::vector<compositionengine::LayerFE::LayerSettings> +ScreenCaptureOutput::generateClientCompositionRequests( + bool supportsProtectedContent, ui::Dataspace outputDataspace, + std::vector<compositionengine::LayerFE*>& outLayerFEs) { + auto clientCompositionLayers = compositionengine::impl::Output:: + generateClientCompositionRequests(supportsProtectedContent, outputDataspace, + outLayerFEs); + + if (mRegionSampling) { + for (auto& layer : clientCompositionLayers) { + layer.backgroundBlurRadius = 0; + layer.blurRegions.clear(); + } + } + + Rect sourceCrop = mRenderArea.getSourceCrop(); + compositionengine::LayerFE::LayerSettings fillLayer; + fillLayer.source.buffer.buffer = nullptr; + fillLayer.source.solidColor = half3(0.0f, 0.0f, 0.0f); + fillLayer.geometry.boundaries = + FloatRect(static_cast<float>(sourceCrop.left), static_cast<float>(sourceCrop.top), + static_cast<float>(sourceCrop.right), static_cast<float>(sourceCrop.bottom)); + fillLayer.alpha = half(RenderArea::getCaptureFillValue(mRenderArea.getCaptureFill())); + clientCompositionLayers.insert(clientCompositionLayers.begin(), fillLayer); + + return clientCompositionLayers; +} + +bool ScreenCaptureOutput::layerNeedsFiltering(const compositionengine::OutputLayer* layer) const { + return mRenderArea.needsFiltering() || + mFilterForScreenshot.find(&layer->getLayerFE()) != mFilterForScreenshot.end(); +} + +} // namespace android diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h new file mode 100644 index 0000000000..61b5ddb1bb --- /dev/null +++ b/services/surfaceflinger/ScreenCaptureOutput.h @@ -0,0 +1,69 @@ +/* + * 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 <compositionengine/DisplayColorProfile.h> +#include <compositionengine/RenderSurface.h> +#include <compositionengine/impl/Output.h> +#include <ui/Rect.h> + +#include "RenderArea.h" + +namespace android { + +struct ScreenCaptureOutputArgs { + const compositionengine::CompositionEngine& compositionEngine; + const compositionengine::Output::ColorProfile& colorProfile; + const RenderArea& renderArea; + ui::LayerStack layerStack; + std::shared_ptr<renderengine::ExternalTexture> buffer; + float sdrWhitePointNits; + float displayBrightnessNits; + std::unordered_set<compositionengine::LayerFE*> filterForScreenshot; + bool regionSampling; +}; + +// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer. +// +// SurfaceFlinger passes instances of ScreenCaptureOutput to CompositionEngine in calls to +// SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay. +class ScreenCaptureOutput : public compositionengine::impl::Output { +public: + ScreenCaptureOutput(ScreenCaptureOutputArgs&&); + + void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override; + + std::vector<compositionengine::LayerFE::LayerSettings> generateClientCompositionRequests( + bool supportsProtectedContent, ui::Dataspace outputDataspace, + std::vector<compositionengine::LayerFE*>& outLayerFEs) override; + + bool layerNeedsFiltering(const compositionengine::OutputLayer*) const override; + +protected: + bool getSkipColorTransform() const override { return false; } + renderengine::DisplaySettings generateClientCompositionDisplaySettings() const override; + +private: + const RenderArea& mRenderArea; + const std::unordered_set<compositionengine::LayerFE*> mFilterForScreenshot; + const compositionengine::Output::ColorProfile& mColorProfile; + const bool mRegionSampling; +}; + +std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs&&); + +} // namespace android diff --git a/services/surfaceflinger/ScreenCaptureRenderSurface.h b/services/surfaceflinger/ScreenCaptureRenderSurface.h new file mode 100644 index 0000000000..20973003d5 --- /dev/null +++ b/services/surfaceflinger/ScreenCaptureRenderSurface.h @@ -0,0 +1,81 @@ +/* + * 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 <memory> + +#include <compositionengine/RenderSurface.h> +#include <renderengine/impl/ExternalTexture.h> +#include <ui/Fence.h> +#include <ui/Size.h> + +namespace android { + +// ScreenCaptureRenderSurface is a RenderSurface that returns a preallocated buffer used by +// ScreenCaptureOutput. +class ScreenCaptureRenderSurface : public compositionengine::RenderSurface { +public: + ScreenCaptureRenderSurface(std::shared_ptr<renderengine::ExternalTexture> buffer) + : mBuffer(std::move(buffer)){}; + + std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer( + base::unique_fd* /* bufferFence */) override { + return mBuffer; + } + + void queueBuffer(base::unique_fd readyFence) override { + mRenderFence = sp<Fence>::make(readyFence.release()); + } + + const sp<Fence>& getClientTargetAcquireFence() const override { return mRenderFence; } + + bool supportsCompositionStrategyPrediction() const override { return false; } + + bool isValid() const override { return true; } + + void initialize() override {} + + const ui::Size& getSize() const override { return mSize; } + + bool isProtected() const override { return mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED; } + + void setDisplaySize(const ui::Size&) override {} + + void setBufferDataspace(ui::Dataspace) override {} + + void setBufferPixelFormat(ui::PixelFormat) override {} + + void setProtected(bool /* useProtected */) override {} + + status_t beginFrame(bool /* mustRecompose */) override { return OK; } + + void prepareFrame(bool /* usesClientComposition */, bool /* usesDeviceComposition */) override { + } + + void onPresentDisplayCompleted() override {} + + void dump(std::string& /* result */) const override {} + +private: + std::shared_ptr<renderengine::ExternalTexture> mBuffer; + + sp<Fence> mRenderFence = Fence::NO_FENCE; + + ui::Size mSize; +}; + +} // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 6a25104a95..d68b00a191 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -44,16 +44,19 @@ #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> +#include <compositionengine/DisplayColorProfileCreationArgs.h> #include <compositionengine/DisplayCreationArgs.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/RenderSurface.h> +#include <compositionengine/impl/DisplayColorProfile.h> #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <configstore/Utils.h> #include <cutils/compiler.h> #include <cutils/properties.h> #include <ftl/algorithm.h> +#include <ftl/concat.h> #include <ftl/fake_guard.h> #include <ftl/future.h> #include <ftl/unit.h> @@ -137,6 +140,7 @@ #include "Scheduler/LayerHistory.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VsyncConfiguration.h" +#include "ScreenCaptureOutput.h" #include "StartPropertySetThread.h" #include "SurfaceFlingerProperties.h" #include "TimeStats/TimeStats.h" @@ -160,6 +164,7 @@ namespace android { using namespace std::chrono_literals; using namespace std::string_literals; +using namespace std::string_view_literals; using namespace hardware::configstore; using namespace hardware::configstore::V1_0; @@ -2179,13 +2184,14 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty; refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty; - std::vector<Layer*> layers; + std::vector<sp<Layer>> layers; mDrawingState.traverseInZOrder([&refreshArgs, &layers](Layer* layer) { + auto strongLayer = sp<Layer>::fromExisting(layer); if (auto layerFE = layer->getCompositionEngineLayerFE()) { layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame); refreshArgs.layers.push_back(layerFE); - layers.push_back(layer); + layers.push_back(std::move(strongLayer)); } }); refreshArgs.blursAreExpensive = mBlursAreExpensive; @@ -2217,8 +2223,8 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) { std::vector<LayerSnapshotGuard> layerSnapshotGuards; - for (Layer* layer : layers) { - layerSnapshotGuards.emplace_back(layer); + for (auto& layer : layers) { + layerSnapshotGuards.emplace_back(layer.get()); } mCompositionEngine->present(refreshArgs); } @@ -2951,6 +2957,8 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, // Display modes are reloaded on hotplug reconnect. if (display->isPrimary()) { + // TODO(b/241285876): Annotate `processDisplayAdded` instead. + ftl::FakeGuard guard(kMainThreadContext); mScheduler->setRefreshRateSelector(selectorPtr); } @@ -3272,7 +3280,7 @@ void SurfaceFlinger::persistDisplayBrightness(bool needsComposite) { ALOGE_IF(error != NO_ERROR, "Error setting display brightness for display %s: %d (%s)", - display->getDebugName().c_str(), error, strerror(error)); + to_string(display->getId()).c_str(), error, strerror(error)); } display->persistBrightness(needsComposite); } @@ -3337,7 +3345,12 @@ void SurfaceFlinger::updateCursorAsync() { std::vector<LayerSnapshotGuard> layerSnapshotGuards; mDrawingState.traverse([&layerSnapshotGuards](Layer* layer) { - if (layer->getLayerSnapshot()->compositionType == + auto strongLayer = sp<Layer>::fromExisting(layer); + const LayerSnapshot* snapshot = layer->getLayerSnapshot(); + if (!snapshot) { + LOG_ALWAYS_FATAL("Layer snapshot unexpectedly null"); + } + if (snapshot->compositionType == aidl::android::hardware::graphics::composer3::Composition::CURSOR) { layer->updateSnapshot(false /* updateGeometry */); layerSnapshotGuards.emplace_back(layer); @@ -3411,19 +3424,18 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { features |= Feature::kPresentFences; } + auto selectorPtr = display->holdRefreshRateSelector(); + if (selectorPtr->kernelIdleTimerController()) { + features |= Feature::kKernelIdleTimer; + } + mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features); - { - auto selectorPtr = display->holdRefreshRateSelector(); - if (selectorPtr->kernelIdleTimerController()) { - features |= Feature::kKernelIdleTimer; - } + mScheduler->createVsyncSchedule(features); + mScheduler->setRefreshRateSelector(selectorPtr); + mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr)); - mScheduler->createVsyncSchedule(features); - mScheduler->setRefreshRateSelector(selectorPtr); - mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr)); - } setVsyncEnabled(false); mScheduler->startTimers(); @@ -4765,17 +4777,18 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { {"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)}, {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)}, {"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)}, - {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })}, {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)}, + {"--events"s, dumper(&SurfaceFlinger::dumpEvents)}, + {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)}, + {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)}, {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)}, {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)}, + {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)}, {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)}, - {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)}, + {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)}, {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)}, - {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)}, - {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)}, }; const auto flag = args.empty() ? ""s : std::string(String8(args[0])); @@ -4899,24 +4912,39 @@ void SurfaceFlinger::appendSfConfigString(std::string& result) const { result.append("]"); } -void SurfaceFlinger::dumpVSync(std::string& result) const { - mScheduler->dump(result); +void SurfaceFlinger::dumpScheduler(std::string& result) const { + utils::Dumper dumper{result}; + + mScheduler->dump(dumper); + + // TODO(b/241286146): Move to Scheduler. + { + utils::Dumper::Indent indent(dumper); + dumper.dump("lastHwcVsyncState"sv, mLastHWCVsyncState); + dumper.dump("pendingHwcVsyncState"sv, mHWCVsyncPendingState); + } + dumper.eol(); + + // TODO(b/241285876): Move to DisplayModeController. + dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor); + dumper.eol(); mRefreshRateStats->dump(result); - result.append("\n"); + dumper.eol(); mVsyncConfiguration->dump(result); StringAppendF(&result, - " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", + " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 + " ns\n\n", dispSyncPresentTimeOffset, getVsyncPeriodFromHWC()); +} - StringAppendF(&result, "(mode override by backdoor: %s)\n\n", - mDebugDisplayModeSetByBackdoor ? "yes" : "no"); - +void SurfaceFlinger::dumpEvents(std::string& result) const { mScheduler->dump(mAppConnectionHandle, result); +} + +void SurfaceFlinger::dumpVsync(std::string& result) const { mScheduler->dumpVsync(result); - StringAppendF(&result, "mHWCVsyncPendingState=%s mLastHWCVsyncState=%s\n", - to_string(mHWCVsyncPendingState).c_str(), to_string(mLastHWCVsyncState).c_str()); } void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const { @@ -4937,19 +4965,21 @@ void SurfaceFlinger::dumpDisplays(std::string& result) const { utils::Dumper dumper{result}; for (const auto& [id, display] : mPhysicalDisplays) { + utils::Dumper::Section section(dumper, ftl::Concat("Display ", id.value).str()); + + display.snapshot().dump(dumper); + if (const auto device = getDisplayDeviceLocked(id)) { device->dump(dumper); } - - utils::Dumper::Indent indent(dumper); - display.snapshot().dump(dumper); - dumper.eol(); } for (const auto& [token, display] : mDisplays) { if (display->isVirtual()) { + const auto displayId = display->getId(); + utils::Dumper::Section section(dumper, + ftl::Concat("Virtual Display ", displayId.value).str()); display->dump(dumper); - dumper.eol(); } } } @@ -5120,7 +5150,7 @@ void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const { Layer::miniDumpHeader(result); const DisplayDevice& ref = *display; - mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); }); + mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); }); result.append("\n"); } } @@ -5160,7 +5190,9 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp colorizer.bold(result); result.append("Scheduler:\n"); colorizer.reset(result); - dumpVSync(result); + dumpScheduler(result); + dumpEvents(result); + dumpVsync(result); result.append("\n"); StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load()); @@ -6250,7 +6282,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, return BAD_VALUE; } - Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height); + Rect layerStackSpaceRect(crop.left, crop.top, crop.left + reqSize.width, + crop.top + reqSize.height); bool childrenOnly = args.childrenOnly; RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> { return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace, @@ -6370,7 +6403,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( ftl::SharedFuture<FenceResult> renderFuture; renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) { - renderFuture = renderScreenImpl(*renderArea, traverseLayers, buffer, + renderFuture = renderScreenImpl(std::move(renderArea), traverseLayers, buffer, canCaptureBlackoutContent, regionSampling, grayscale, captureResults); }); @@ -6398,19 +6431,19 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( } ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( - const RenderArea& renderArea, TraverseLayersFunction traverseLayers, + std::unique_ptr<RenderArea> renderArea, TraverseLayersFunction traverseLayers, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool canCaptureBlackoutContent, bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) { ATRACE_CALL(); + size_t layerCount = 0; traverseLayers([&](Layer* layer) { + layerCount++; captureResults.capturedSecureLayers = captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure()); }); - const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED; - // We allow the system server to take screenshots of secure layers for // use in situations like the Screen-rotation animation and place // the impetus on WindowManager to not persist them. @@ -6420,8 +6453,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( } captureResults.buffer = buffer->getBuffer(); - auto dataspace = renderArea.getReqDataSpace(); - auto parent = renderArea.getParentLayer(); + auto dataspace = renderArea->getReqDataSpace(); + auto parent = renderArea->getParentLayer(); auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC; auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance; auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance; @@ -6443,121 +6476,107 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( } captureResults.capturedDataspace = dataspace; - const auto reqWidth = renderArea.getReqWidth(); - const auto reqHeight = renderArea.getReqHeight(); - const auto sourceCrop = renderArea.getSourceCrop(); - const auto transform = renderArea.getTransform(); - const auto rotation = renderArea.getRotationFlags(); - const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect(); - - renderengine::DisplaySettings clientCompositionDisplay; - std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers; - - // assume that bounds are never offset, and that they are the same as the - // buffer bounds. - clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight); - clientCompositionDisplay.clip = sourceCrop; - clientCompositionDisplay.orientation = rotation; - - clientCompositionDisplay.outputDataspace = dataspace; - clientCompositionDisplay.currentLuminanceNits = displayBrightnessNits; - clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance; - clientCompositionDisplay.renderIntent = - static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(renderIntent); - - const float colorSaturation = grayscale ? 0 : 1; - clientCompositionDisplay.colorTransform = calculateColorMatrix(colorSaturation); - - const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill()); - - compositionengine::LayerFE::LayerSettings fillLayer; - fillLayer.source.buffer.buffer = nullptr; - fillLayer.source.solidColor = half3(0.0, 0.0, 0.0); - fillLayer.geometry.boundaries = - FloatRect(sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom); - fillLayer.alpha = half(alpha); - clientCompositionLayers.push_back(fillLayer); - - const auto display = renderArea.getDisplayDevice(); - std::vector<Layer*> renderedLayers; - bool disableBlurs = false; - traverseLayers([&](Layer* layer) FTL_FAKE_GUARD(kMainThreadContext) { - auto layerFE = layer->getCompositionEngineLayerFE(); - if (!layerFE) { - return; - } + const auto transform = renderArea->getTransform(); + const auto display = renderArea->getDisplayDevice(); + + std::vector<std::pair<Layer*, sp<LayerFE>>> layers; + layers.reserve(layerCount); + std::unordered_set<compositionengine::LayerFE*> filterForScreenshot; + traverseLayers([&](Layer* layer) { + auto strongLayer = sp<Layer>::fromExisting(layer); + captureResults.capturedHdrLayers |= isHdrLayer(layer); // Layer::prepareClientComposition uses the layer's snapshot to populate the resulting // LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are // generated with the layer's current buffer and geometry. layer->updateSnapshot(true /* updateGeometry */); - disableBlurs |= layer->getDrawingState().sidebandStream != nullptr; - - Region clip(renderArea.getBounds()); - compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{ - clip, - layer->needsFilteringForScreenshots(display.get(), transform) || - renderArea.needsFiltering(), - renderArea.isSecure(), - useProtected, - layerStackSpaceRect, - clientCompositionDisplay.outputDataspace, - true, /* realContentIsVisible */ - false, /* clearContent */ - disableBlurs ? compositionengine::LayerFE::ClientCompositionTargetSettings:: - BlurSetting::Disabled - : compositionengine::LayerFE::ClientCompositionTargetSettings:: - BlurSetting::Enabled, - isHdrLayer(layer) ? displayBrightnessNits : sdrWhitePointNits, + layers.emplace_back(layer, layer->copyCompositionEngineLayerFE()); - }; - std::optional<compositionengine::LayerFE::LayerSettings> settings; - { - LayerSnapshotGuard layerSnapshotGuard(layer); - settings = layerFE->prepareClientComposition(targetSettings); - } + sp<LayerFE>& layerFE = layers.back().second; - if (!settings) { - return; - } + layerFE->mSnapshot->geomLayerTransform = + renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform; - settings->geometry.positionTransform = - transform.asMatrix4() * settings->geometry.positionTransform; - // There's no need to process blurs when we're executing region sampling, - // we're just trying to understand what we're drawing, and doing so without - // blurs is already a pretty good approximation. - if (regionSampling) { - settings->backgroundBlurRadius = 0; - settings->blurRegions.clear(); + if (layer->needsFilteringForScreenshots(display.get(), transform)) { + filterForScreenshot.insert(layerFE.get()); } - captureResults.capturedHdrLayers |= isHdrLayer(layer); - - clientCompositionLayers.push_back(std::move(*settings)); - renderedLayers.push_back(layer); }); - std::vector<renderengine::LayerSettings> clientRenderEngineLayers; - clientRenderEngineLayers.reserve(clientCompositionLayers.size()); - std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(), - std::back_inserter(clientRenderEngineLayers), - [](compositionengine::LayerFE::LayerSettings& settings) - -> renderengine::LayerSettings { return settings; }); + ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK}; + if (!layers.empty()) { + const sp<LayerFE>& layerFE = layers.back().second; + layerStack = layerFE->getCompositionState()->outputFilter.layerStack; + } - // Use an empty fence for the buffer fence, since we just created the buffer so - // there is no need for synchronization with the GPU. - base::unique_fd bufferFence; + auto copyLayerFEs = [&layers]() { + std::vector<sp<compositionengine::LayerFE>> layerFEs; + layerFEs.reserve(layers.size()); + for (const auto& [_, layerFE] : layers) { + layerFEs.push_back(layerFE); + } + return layerFEs; + }; - constexpr bool kUseFramebufferCache = false; - const auto future = getRenderEngine() - .drawLayers(clientCompositionDisplay, clientRenderEngineLayers, - buffer, kUseFramebufferCache, std::move(bufferFence)) - .share(); + auto present = [this, buffer = std::move(buffer), dataspace, sdrWhitePointNits, + displayBrightnessNits, filterForScreenshot = std::move(filterForScreenshot), + grayscale, layerFEs = copyLayerFEs(), layerStack, regionSampling, + renderArea = std::move(renderArea), renderIntent]() -> FenceResult { + std::unique_ptr<compositionengine::CompositionEngine> compositionEngine = + mFactory.createCompositionEngine(); + compositionEngine->setRenderEngine(mRenderEngine.get()); + + compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace, + .renderIntent = renderIntent}; + + std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput( + ScreenCaptureOutputArgs{.compositionEngine = *compositionEngine, + .colorProfile = colorProfile, + .renderArea = *renderArea, + .layerStack = layerStack, + .buffer = std::move(buffer), + .sdrWhitePointNits = sdrWhitePointNits, + .displayBrightnessNits = displayBrightnessNits, + .filterForScreenshot = std::move(filterForScreenshot), + .regionSampling = regionSampling}); + + const float colorSaturation = grayscale ? 0 : 1; + compositionengine::CompositionRefreshArgs refreshArgs{ + .outputs = {output}, + .layers = std::move(layerFEs), + .updatingOutputGeometryThisFrame = true, + .updatingGeometryThisFrame = true, + .colorTransformMatrix = calculateColorMatrix(colorSaturation), + }; + compositionEngine->present(refreshArgs); + + return output->getRenderSurface()->getClientTargetAcquireFence(); + }; - for (auto* layer : renderedLayers) { - layer->onLayerDisplayed(future); + // If RenderEngine is threaded, we can safely call CompositionEngine::present off the main + // thread as the RenderEngine::drawLayers call will run on RenderEngine's thread. Otherwise, + // we need RenderEngine to run on the main thread so we call CompositionEngine::present + // immediately. + // + // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call + // to CompositionEngine::present. + const bool renderEngineIsThreaded = [&]() { + using Type = renderengine::RenderEngine::RenderEngineType; + const auto type = mRenderEngine->getRenderEngineType(); + return type == Type::THREADED || type == Type::SKIA_GL_THREADED; + }(); + auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share() + : ftl::yield(present()).share(); + + for (auto& [layer, layerFE] : layers) { + layer->onLayerDisplayed( + ftl::Future(presentFuture) + .then([layerFE = std::move(layerFE)](FenceResult) { + return layerFE->stealCompositionResult().releaseFences.back().get(); + }) + .share()); } - return future; + return presentFuture; } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 3d93d90b30..fd96fbb8f6 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -685,7 +685,7 @@ private: void commitInputWindowCommands() REQUIRES(mStateLock); void updateCursorAsync(); - void initScheduler(const sp<const DisplayDevice>&) REQUIRES(mStateLock); + void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock); void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock); void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod); @@ -780,7 +780,7 @@ private: const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, const sp<IScreenCaptureListener>&); ftl::SharedFuture<FenceResult> renderScreenImpl( - const RenderArea&, TraverseLayersFunction, + std::unique_ptr<RenderArea>, TraverseLayersFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent, bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); @@ -1013,7 +1013,9 @@ private: void dumpFrameTimeline(const DumpArgs& args, std::string& result) const; void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext); - void dumpVSync(std::string& result) const REQUIRES(mStateLock); + void dumpScheduler(std::string& result) const REQUIRES(mStateLock); + void dumpEvents(std::string& result) const REQUIRES(mStateLock); + void dumpVsync(std::string& result) const REQUIRES(mStateLock); void dumpCompositionDisplays(std::string& result) const REQUIRES(mStateLock); void dumpDisplays(std::string& result) const REQUIRES(mStateLock); diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h index 3761f9e806..ee942177e5 100644 --- a/services/surfaceflinger/Utils/Dumper.h +++ b/services/surfaceflinger/Utils/Dumper.h @@ -16,10 +16,11 @@ #pragma once -#include <optional> #include <string> #include <string_view> +#include <ftl/optional.h> + namespace android::utils { // Dumps variables by appending their name and value to the output string. A variable is formatted @@ -44,16 +45,48 @@ public: eol(); } + void dump(std::string_view name, const std::string& value) { + dump(name, static_cast<const std::string_view&>(value)); + } + void dump(std::string_view name, bool value) { using namespace std::string_view_literals; dump(name, value ? "true"sv : "false"sv); } template <typename T> - void dump(std::string_view name, const std::optional<T>& value) { - using namespace std::string_view_literals; + void dump(std::string_view name, const std::optional<T>& opt) { + if (opt) { + dump(name, *opt); + } else { + using namespace std::string_view_literals; + dump(name, "nullopt"sv); + } + } + + template <typename T> + void dump(std::string_view name, const ftl::Optional<T>& opt) { + dump(name, static_cast<const std::optional<T>&>(opt)); + } + + template <typename T, typename... Ts> + void dump(std::string_view name, const T& value, const Ts&... rest) { + std::string string; + + constexpr bool kIsTuple = sizeof...(Ts) > 0; + if constexpr (kIsTuple) { + string += '{'; + } + using std::to_string; - dump(name, value ? to_string(*value) : "nullopt"sv); + string += to_string(value); + + if constexpr (kIsTuple) { + string += ((", " + to_string(rest)) + ...); + string += '}'; + } + + dump(name, string); } struct Indent { @@ -63,6 +96,21 @@ public: Dumper& dumper; }; + struct Section { + Section(Dumper& dumper, std::string_view heading) : dumper(dumper) { + dumper.dump({}, heading); + indent.emplace(dumper); + } + + ~Section() { + indent.reset(); + dumper.eol(); + } + + Dumper& dumper; + std::optional<Indent> indent; + }; + private: std::string& mOut; int mIndent = 0; diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index f2d008d59f..bd11a37a06 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -249,7 +249,9 @@ void SchedulerFuzzer::fuzzLayerHistory() { scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); - dump<scheduler::TestableScheduler>(scheduler, &mFdp); + std::string result = mFdp.ConsumeRandomLengthString(kRandomStringLength); + utils::Dumper dumper(result); + scheduler->dump(dumper); } void SchedulerFuzzer::fuzzVSyncReactor() { diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 7148c117c5..06b9caa7cb 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -244,8 +244,8 @@ void CompositionTest::captureScreenComposition() { HAL_PIXEL_FORMAT_RGBA_8888, 1, usage); - auto future = mFlinger.renderScreenImpl(*renderArea, traverseLayers, mCaptureScreenBuffer, - forSystem, regionSampling); + auto future = mFlinger.renderScreenImpl(std::move(renderArea), traverseLayers, + mCaptureScreenBuffer, forSystem, regionSampling); ASSERT_TRUE(future.valid()); const auto fenceResult = future.get(); diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 95c99150b3..ba214d534f 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -17,6 +17,7 @@ #pragma once #include <Scheduler/Scheduler.h> +#include <ftl/fake_guard.h> #include <gmock/gmock.h> #include <gui/ISurfaceComposer.h> @@ -69,6 +70,11 @@ public: auto refreshRateSelector() { return holdRefreshRateSelector(); } bool hasRefreshRateSelectors() const { return !mRefreshRateSelectors.empty(); } + void setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) { + ftl::FakeGuard guard(kMainThreadContext); + return Scheduler::setRefreshRateSelector(std::move(selectorPtr)); + } + auto& mutableLayerHistory() { return mLayerHistory; } size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 46eca69c6a..df53f1560a 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -37,6 +37,7 @@ #include "FrontEnd/LayerHandle.h" #include "Layer.h" #include "NativeWindowSurface.h" +#include "RenderArea.h" #include "Scheduler/MessageQueue.h" #include "Scheduler/RefreshRateSelector.h" #include "StartPropertySetThread.h" @@ -399,14 +400,14 @@ public: return mFlinger->setPowerModeInternal(display, mode); } - auto renderScreenImpl(const RenderArea& renderArea, - SurfaceFlinger::TraverseLayersFunction traverseLayers, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, - bool forSystem, bool regionSampling) { + auto renderScreenImpl(std::unique_ptr<RenderArea> renderArea, + SurfaceFlinger::TraverseLayersFunction traverseLayers, + const std::shared_ptr<renderengine::ExternalTexture>& buffer, + bool forSystem, bool regionSampling) { ScreenCaptureResults captureResults; return FTL_FAKE_GUARD(kMainThreadContext, - mFlinger->renderScreenImpl(renderArea, traverseLayers, buffer, - forSystem, regionSampling, + mFlinger->renderScreenImpl(std::move(renderArea), traverseLayers, + buffer, forSystem, regionSampling, false /* grayscale */, captureResults)); } diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h index 5f749dfbcc..f4ded216cb 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h @@ -42,6 +42,7 @@ public: MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override)); MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override)); MOCK_METHOD(Status, sendHint, (SessionHint), (override)); + MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (override)); }; } // namespace android::Hwc2::mock diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index abcac3c10e..87b3a89cce 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -787,13 +787,14 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, std::vector<VkSurfaceFormatKHR> all_formats = { {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - // Also allow to use PASS_THROUGH + HAL_DATASPACE_ARBITRARY - {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}, - {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}, }; if (colorspace_ext) { all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); } @@ -812,16 +813,22 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + if (colorspace_ext) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R5G6B5_UNORM_PACK16, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); + } } desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + if (colorspace_ext) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); + } if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, @@ -837,9 +844,11 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); + if (colorspace_ext) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); + } if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, @@ -849,9 +858,10 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, desc.format = AHARDWAREBUFFER_FORMAT_R8_UNORM; if (AHardwareBuffer_isSupported(&desc)) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R8_UNORM, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); + if (colorspace_ext) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}); + } } // NOTE: Any new formats that are added must be coordinated across different |