diff options
177 files changed, 5005 insertions, 1392 deletions
diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl index 248e9738df..9b11109262 100644 --- a/aidl/binder/android/os/PersistableBundle.aidl +++ b/aidl/binder/android/os/PersistableBundle.aidl @@ -17,4 +17,4 @@ package android.os; -@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h" ndk_header "android/persistable_bundle_aidl.h"; +@JavaOnlyStableParcelable @NdkOnlyStableParcelable @RustOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h" ndk_header "android/persistable_bundle_aidl.h" rust_type "binder::PersistableBundle"; diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index feca18d810..888fb67b31 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -194,7 +194,6 @@ void add_mountinfo(); #define CGROUPFS_DIR "/sys/fs/cgroup" #define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk" #define DROPBOX_DIR "/data/system/dropbox" -#define PRINT_FLAGS "/system/bin/printflags" #define UWB_LOG_DIR "/data/misc/apexdata/com.android.uwb/log" // TODO(narayan): Since this information has to be kept in sync @@ -1823,12 +1822,8 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { DumpFile("PRODUCT BUILD-TIME RELEASE FLAGS", "/product/etc/build_flags.json"); DumpFile("VENDOR BUILD-TIME RELEASE FLAGS", "/vendor/etc/build_flags.json"); - RunCommand("ACONFIG FLAGS", {PRINT_FLAGS}, - CommandOptions::WithTimeout(10).Always().DropRoot().Build()); RunCommand("ACONFIG FLAGS DUMP", {AFLAGS, "list"}, CommandOptions::WithTimeout(10).Always().AsRootIfAvailable().Build()); - RunCommand("WHICH ACONFIG FLAG STORAGE", {AFLAGS, "which-backing"}, - CommandOptions::WithTimeout(10).Always().AsRootIfAvailable().Build()); RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 4486bd6d3e..db565513c9 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -119,7 +119,6 @@ static constexpr const char* kFuseProp = "persist.sys.fuse"; */ static constexpr const char* kAppDataIsolationEnabledProperty = "persist.zygote.app_data_isolation"; static constexpr const char* kMntSdcardfs = "/mnt/runtime/default/"; -static constexpr const char* kMntFuse = "/mnt/pass_through/0/"; static std::atomic<bool> sAppDataIsolationEnabled(false); @@ -3697,7 +3696,9 @@ binder::Status InstalldNativeService::invalidateMounts() { std::getline(in, ignored); if (android::base::GetBoolProperty(kFuseProp, false)) { - if (target.find(kMntFuse) == 0) { + const std::regex kMntFuseRe = + std::regex(R"(^/mnt/pass_through/(0|[0-9]+/[A-Z0-9]{4}-[A-Z0-9]{4}).*)"); + if (std::regex_match(target, kMntFuseRe)) { LOG(DEBUG) << "Found storage mount " << source << " at " << target; mStorageMounts[source] = target; } diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index c86adef118..c818e0d7ba 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -16,6 +16,7 @@ #include <fcntl.h> #include <linux/unistd.h> +#include <sched.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/wait.h> diff --git a/include/audiomanager/OWNERS b/include/audiomanager/OWNERS index 2bd527cc3f..58257baf20 100644 --- a/include/audiomanager/OWNERS +++ b/include/audiomanager/OWNERS @@ -1,2 +1,3 @@ +atneya@google.com elaurent@google.com jmtrivi@google.com diff --git a/include/input/DisplayTopologyGraph.h b/include/input/DisplayTopologyGraph.h index 90427bd76d..f3f5148540 100644 --- a/include/input/DisplayTopologyGraph.h +++ b/include/input/DisplayTopologyGraph.h @@ -43,7 +43,7 @@ enum class DisplayTopologyPosition : int32_t { struct DisplayTopologyAdjacentDisplay { ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID; DisplayTopologyPosition position; - float offsetPx; + float offsetDp; }; /** @@ -52,6 +52,7 @@ struct DisplayTopologyAdjacentDisplay { struct DisplayTopologyGraph { ui::LogicalDisplayId primaryDisplayId = ui::LogicalDisplayId::INVALID; std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> graph; + std::unordered_map<ui::LogicalDisplayId, int> displaysDensity; }; } // namespace android diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index aac369ddd1..fb00d4f468 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -269,6 +269,7 @@ cc_defaults { "-Wzero-as-null-pointer-constant", "-Wreorder-init-list", "-Wunused-const-variable", + "-Wunused-result", "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", // Hide symbols by default and set the BUILDING_LIBBINDER macro so that diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 0a22588a90..bc7ae37ff0 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -38,6 +38,7 @@ #endif #include "BuildFlags.h" +#include "Constants.h" #include "OS.h" #include "RpcState.h" @@ -70,8 +71,6 @@ constexpr bool kEnableRecording = true; constexpr bool kEnableRecording = false; #endif -// Log any reply transactions for which the data exceeds this size -#define LOG_REPLIES_OVER_SIZE (300 * 1024) // --------------------------------------------------------------------------- IBinder::IBinder() @@ -412,7 +411,7 @@ status_t BBinder::transact( // In case this is being transacted on in the same process. if (reply != nullptr) { reply->setDataPosition(0); - if (reply->dataSize() > LOG_REPLIES_OVER_SIZE) { + if (reply->dataSize() > binder::kLogTransactionsOverBytes) { ALOGW("Large reply transaction of %zu bytes, interface descriptor %s, code %d", reply->dataSize(), String8(getInterfaceDescriptor()).c_str(), code); } diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 444f06174e..c13e0f9e9c 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -28,6 +28,7 @@ #include <stdio.h> #include "BuildFlags.h" +#include "Constants.h" #include "file.h" //#undef ALOGV @@ -63,9 +64,6 @@ std::atomic<uint32_t> BpBinder::sBinderProxyCountWarned(0); static constexpr uint32_t kBinderProxyCountWarnInterval = 5000; -// Log any transactions for which the data exceeds this size -#define LOG_TRANSACTIONS_OVER_SIZE (300 * 1024) - enum { LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached WARNING_REACHED_MASK = 0x40000000, // A flag denoting that the warning has been reached @@ -403,9 +401,11 @@ status_t BpBinder::transact( status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } - if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) { + + if (data.dataSize() > binder::kLogTransactionsOverBytes) { RpcMutexUniqueLock _l(mLock); - ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d", + ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d was " + "sent", data.dataSize(), String8(mDescriptorCache).c_str(), code); } diff --git a/libs/binder/Constants.h b/libs/binder/Constants.h new file mode 100644 index 0000000000..b75493cb8a --- /dev/null +++ b/libs/binder/Constants.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2025 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 + +namespace android::binder { + +/** + * See also BINDER_VM_SIZE. In kernel binder, the sum of all transactions must be allocated in this + * space. Large transactions are very error prone. In general, we should work to reduce this limit. + * The same limit is used in RPC binder for consistency. + */ +constexpr size_t kLogTransactionsOverBytes = 300 * 1024; + +/** + * See b/392575419 - this limit is chosen for a specific usecase, because RPC binder does not have + * support for shared memory in the Android Baklava timeframe. This was 100 KB during and before + * Android V. + * + * Keeping this low helps preserve overall system performance. Transactions of this size are far too + * expensive to make multiple copies over binder or sockets, and they should be avoided if at all + * possible and transition to shared memory. + */ +constexpr size_t kRpcTransactionLimitBytes = 600 * 1024; + +} // namespace android::binder diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 623e7b9139..1c1b6f30a4 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -38,6 +38,10 @@ #include "Utils.h" #include "binder_module.h" +#if (defined(__ANDROID__) || defined(__Fuchsia__)) && !defined(BINDER_WITH_KERNEL_IPC) +#error Android and Fuchsia are expected to have BINDER_WITH_KERNEL_IPC +#endif + #if LOG_NDEBUG #define IF_LOG_TRANSACTIONS() if (false) @@ -626,12 +630,22 @@ void IPCThreadState::flushCommands() { if (mProcess->mDriverFD < 0) return; - talkWithDriver(false); + + if (status_t res = talkWithDriver(false); res != OK) { + // TODO: we may want to abort for some of these cases + ALOGW("1st call to talkWithDriver returned error in flushCommands: %s", + statusToString(res).c_str()); + } + // The flush could have caused post-write refcount decrements to have // been executed, which in turn could result in BC_RELEASE/BC_DECREFS // being queued in mOut. So flush again, if we need to. if (mOut.dataSize() > 0) { - talkWithDriver(false); + if (status_t res = talkWithDriver(false); res != OK) { + // TODO: we may want to abort for some of these cases + ALOGW("2nd call to talkWithDriver returned error in flushCommands: %s", + statusToString(res).c_str()); + } } if (mOut.dataSize() > 0) { ALOGW("mOut.dataSize() > 0 after flushCommands()"); @@ -803,7 +817,11 @@ void IPCThreadState::joinThreadPool(bool isMain) mOut.writeInt32(BC_EXIT_LOOPER); mIsLooper = false; - talkWithDriver(false); + if (status_t res = talkWithDriver(false); res != OK) { + // TODO: we may want to abort for some of these cases + ALOGW("call to talkWithDriver in joinThreadPool returned error: %s, FD: %d", + statusToString(res).c_str(), mProcess->mDriverFD); + } size_t oldCount = mProcess->mCurrentThreads.fetch_sub(1); LOG_ALWAYS_FATAL_IF(oldCount == 0, "Threadpool thread count underflowed. Thread cannot exist and exit in " @@ -840,7 +858,7 @@ status_t IPCThreadState::handlePolledCommands() void IPCThreadState::stopProcess(bool /*immediate*/) { //ALOGI("**** STOPPING PROCESS"); - flushCommands(); + (void)flushCommands(); int fd = mProcess->mDriverFD; mProcess->mDriverFD = -1; close(fd); @@ -1215,7 +1233,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) std::string message = logStream.str(); ALOGI("%s", message.c_str()); } -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) err = NO_ERROR; else @@ -1494,7 +1512,14 @@ status_t IPCThreadState::executeCommand(int32_t cmd) buffer.setDataSize(0); constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF; - sendReply(reply, (tr.flags & kForwardReplyFlags)); + + // TODO: we may want to abort if there is an error here, or return as 'error' + // from this function, but the impact needs to be measured + status_t error2 = sendReply(reply, (tr.flags & kForwardReplyFlags)); + if (error2 != OK) { + ALOGE("error in sendReply for synchronous call: %s", + statusToString(error2).c_str()); + } } else { if (error != OK) { std::ostringstream logStream; @@ -1604,7 +1629,7 @@ void IPCThreadState::threadDestructor(void *st) IPCThreadState* const self = static_cast<IPCThreadState*>(st); if (self) { self->flushCommands(); -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (self->mProcess->mDriverFD >= 0) { ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); } @@ -1620,7 +1645,7 @@ status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received binder_frozen_status_info info = {}; info.pid = pid; -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0) ret = -errno; #endif @@ -1639,7 +1664,7 @@ status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { info.timeout_ms = timeout_ms; -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0) ret = -errno; #endif @@ -1657,7 +1682,7 @@ void IPCThreadState::logExtendedError() { if (!ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::EXTENDED_ERROR)) return; -#if defined(__ANDROID__) +#if defined(BINDER_WITH_KERNEL_IPC) if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0) { ALOGE("Failed to get extended error: %s", strerror(errno)); return; diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 719e445794..c9ca646472 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -43,7 +43,11 @@ #include <binder/IPermissionController.h> #endif -#ifdef __ANDROID__ +#if !(defined(__ANDROID__) || defined(__FUCHSIA)) +#define BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT +#endif + +#if !defined(BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT) #include <cutils/properties.h> #else #include "ServiceManagerHost.h" @@ -902,7 +906,7 @@ std::vector<IServiceManager::ServiceDebugInfo> CppBackendShim::getServiceDebugIn return ret; } -#ifndef __ANDROID__ +#if defined(BINDER_SERVICEMANAGEMENT_DELEGATION_SUPPORT) // CppBackendShim for host. Implements the old libbinder android::IServiceManager API. // The internal implementation of the AIDL interface android::os::IServiceManager calls into // on-device service manager. diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index c0ebee006b..777c22a63e 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -299,8 +299,13 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { obj.handle = handle; obj.cookie = 0; } else { +#if __linux__ int policy = local->getMinSchedulerPolicy(); int priority = local->getMinSchedulerPriority(); +#else + int policy = 0; + int priority = 0; +#endif if (policy != 0 || priority != 0) { // override value, since it is set explicitly @@ -616,6 +621,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; return INVALID_OPERATION; #endif // BINDER_WITH_KERNEL_IPC } else { @@ -797,6 +803,7 @@ std::vector<int> Parcel::debugReadAllFileDescriptors() const { setDataPosition(initPosition); #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; #endif } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) { for (const auto& fd : *rpcFields->mFds) { @@ -839,9 +846,10 @@ status_t Parcel::hasBindersInRange(size_t offset, size_t len, bool* result) cons } #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; return INVALID_OPERATION; #endif // BINDER_WITH_KERNEL_IPC - } else if (const auto* rpcFields = maybeRpcFields()) { + } else if (maybeRpcFields()) { return INVALID_OPERATION; } return NO_ERROR; @@ -879,6 +887,7 @@ status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* resu } #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; return INVALID_OPERATION; #endif // BINDER_WITH_KERNEL_IPC } else if (const auto* rpcFields = maybeRpcFields()) { @@ -971,6 +980,7 @@ status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { writeInt32(kHeader); #else // BINDER_WITH_KERNEL_IPC LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; return INVALID_OPERATION; #endif // BINDER_WITH_KERNEL_IPC } @@ -1061,6 +1071,7 @@ bool Parcel::enforceInterface(const char16_t* interface, #else // BINDER_WITH_KERNEL_IPC LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); (void)threadState; + (void)kernelFields; return false; #endif // BINDER_WITH_KERNEL_IPC } @@ -2688,6 +2699,7 @@ void Parcel::closeFileDescriptors(size_t newObjectsSize) { #else // BINDER_WITH_KERNEL_IPC (void)newObjectsSize; LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)kernelFields; #endif // BINDER_WITH_KERNEL_IPC } else if (auto* rpcFields = maybeRpcFields()) { rpcFields->mFds.reset(); diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 0e1e9b4127..0bec37999b 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -48,6 +48,10 @@ #define DEFAULT_MAX_BINDER_THREADS 15 #define DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION 1 +#if defined(__ANDROID__) || defined(__Fuchsia__) +#define EXPECT_BINDER_OPEN_SUCCESS +#endif + #ifdef __ANDROID_VNDK__ const char* kDefaultDriver = "/dev/vndbinder"; #else @@ -613,7 +617,7 @@ ProcessState::ProcessState(const char* driver) } } -#ifdef __ANDROID__ +#if defined(EXPECT_BINDER_OPEN_SUCCESS) LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Error: %s. Terminating.", driver, error.c_str()); diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index fe6e1a3318..03d974d186 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -23,6 +23,7 @@ #include <binder/IPCThreadState.h> #include <binder/RpcServer.h> +#include "Constants.h" #include "Debug.h" #include "RpcWireFormat.h" #include "Utils.h" @@ -337,6 +338,8 @@ std::string RpcState::BinderNode::toString() const { } RpcState::CommandData::CommandData(size_t size) : mSize(size) { + if (size == 0) return; + // The maximum size for regular binder is 1MB for all concurrent // transactions. A very small proportion of transactions are even // larger than a page, but we need to avoid allocating too much @@ -348,11 +351,11 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { // transaction (in some cases, additional fixed size amounts are added), // though for rough consistency, we should avoid cases where this data type // is used for multiple dynamic allocations for a single transaction. - constexpr size_t kMaxTransactionAllocation = 100 * 1000; - if (size == 0) return; - if (size > kMaxTransactionAllocation) { - ALOGW("Transaction requested too much data allocation %zu", size); + if (size > binder::kRpcTransactionLimitBytes) { + ALOGE("Transaction requested too much data allocation: %zu bytes, failing.", size); return; + } else if (size > binder::kLogTransactionsOverBytes) { + ALOGW("Transaction too large: inefficient and in danger of breaking: %zu bytes.", size); } mData.reset(new (std::nothrow) uint8_t[size]); } diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h index 99fa6b8b29..51b9716bb1 100644 --- a/libs/binder/include/binder/RpcThreads.h +++ b/libs/binder/include/binder/RpcThreads.h @@ -20,6 +20,7 @@ #include <condition_variable> #include <functional> #include <memory> +#include <mutex> #include <thread> #include <binder/Common.h> diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 8404a48c26..adef9ea64b 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -16,6 +16,7 @@ rust_library { "libdowncast_rs", "liblibc", "liblog_rust", + "libzerocopy", ], host_supported: true, vendor_available: true, @@ -205,6 +206,7 @@ rust_test { "libdowncast_rs", "liblibc", "liblog_rust", + "libzerocopy", ], } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index e08a76312d..0026f213f2 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -99,6 +99,8 @@ mod binder_async; mod error; mod native; mod parcel; +#[cfg(not(trusty))] +mod persistable_bundle; mod proxy; #[cfg(not(any(trusty, android_ndk)))] mod service; @@ -113,6 +115,8 @@ pub use crate::binder_async::{BinderAsyncPool, BoxFuture}; pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode}; pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder}; +#[cfg(not(trusty))] +pub use persistable_bundle::{PersistableBundle, ValueType}; pub use proxy::{DeathRecipient, SpIBinder, WpIBinder}; #[cfg(not(any(trusty, android_ndk)))] pub use service::{ diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index 485b0bdb0d..2d40ced2fd 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -184,7 +184,7 @@ unsafe impl AsNative<sys::AParcel> for Parcel { /// Safety: The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` /// object will always contain a valid pointer to an `AParcel`. -unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { +unsafe impl AsNative<sys::AParcel> for BorrowedParcel<'_> { fn as_native(&self) -> *const sys::AParcel { self.ptr.as_ptr() } @@ -195,7 +195,7 @@ unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { } // Data serialization methods -impl<'a> BorrowedParcel<'a> { +impl BorrowedParcel<'_> { /// Data written to parcelable is zero'd before being deleted or reallocated. #[cfg(not(android_ndk))] pub fn mark_sensitive(&mut self) { @@ -334,7 +334,7 @@ impl<'a> BorrowedParcel<'a> { /// A segment of a writable parcel, used for [`BorrowedParcel::sized_write`]. pub struct WritableSubParcel<'a>(BorrowedParcel<'a>); -impl<'a> WritableSubParcel<'a> { +impl WritableSubParcel<'_> { /// Write a type that implements [`Serialize`] to the sub-parcel. pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { parcelable.serialize(&mut self.0) @@ -440,7 +440,7 @@ impl Parcel { } // Data deserialization methods -impl<'a> BorrowedParcel<'a> { +impl BorrowedParcel<'_> { /// Attempt to read a type that implements [`Deserialize`] from this parcel. pub fn read<D: Deserialize>(&self) -> Result<D> { D::deserialize(self) @@ -565,7 +565,7 @@ pub struct ReadableSubParcel<'a> { end_position: i32, } -impl<'a> ReadableSubParcel<'a> { +impl ReadableSubParcel<'_> { /// Read a type that implements [`Deserialize`] from the sub-parcel. pub fn read<D: Deserialize>(&self) -> Result<D> { D::deserialize(&self.parcel) @@ -649,7 +649,7 @@ impl Parcel { } // Internal APIs -impl<'a> BorrowedParcel<'a> { +impl BorrowedParcel<'_> { pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> { // Safety: `BorrowedParcel` always contains a valid pointer to an // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return @@ -702,7 +702,7 @@ impl fmt::Debug for Parcel { } } -impl<'a> fmt::Debug for BorrowedParcel<'a> { +impl fmt::Debug for BorrowedParcel<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BorrowedParcel").finish() } diff --git a/libs/binder/rust/src/persistable_bundle.rs b/libs/binder/rust/src/persistable_bundle.rs new file mode 100644 index 0000000000..8639c0d08c --- /dev/null +++ b/libs/binder/rust/src/persistable_bundle.rs @@ -0,0 +1,1076 @@ +/* + * Copyright (C) 2025 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. + */ + +use crate::{ + binder::AsNative, + error::{status_result, StatusCode}, + impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable, + parcel::{BorrowedParcel, UnstructuredParcelable}, +}; +use binder_ndk_sys::{ + APersistableBundle, APersistableBundle_delete, APersistableBundle_dup, + APersistableBundle_erase, APersistableBundle_getBoolean, APersistableBundle_getBooleanKeys, + APersistableBundle_getBooleanVector, APersistableBundle_getBooleanVectorKeys, + APersistableBundle_getDouble, APersistableBundle_getDoubleKeys, + APersistableBundle_getDoubleVector, APersistableBundle_getDoubleVectorKeys, + APersistableBundle_getInt, APersistableBundle_getIntKeys, APersistableBundle_getIntVector, + APersistableBundle_getIntVectorKeys, APersistableBundle_getLong, + APersistableBundle_getLongKeys, APersistableBundle_getLongVector, + APersistableBundle_getLongVectorKeys, APersistableBundle_getPersistableBundle, + APersistableBundle_getPersistableBundleKeys, APersistableBundle_getString, + APersistableBundle_getStringKeys, APersistableBundle_getStringVector, + APersistableBundle_getStringVectorKeys, APersistableBundle_isEqual, APersistableBundle_new, + APersistableBundle_putBoolean, APersistableBundle_putBooleanVector, + APersistableBundle_putDouble, APersistableBundle_putDoubleVector, APersistableBundle_putInt, + APersistableBundle_putIntVector, APersistableBundle_putLong, APersistableBundle_putLongVector, + APersistableBundle_putPersistableBundle, APersistableBundle_putString, + APersistableBundle_putStringVector, APersistableBundle_readFromParcel, APersistableBundle_size, + APersistableBundle_writeToParcel, APERSISTABLEBUNDLE_ALLOCATOR_FAILED, + APERSISTABLEBUNDLE_KEY_NOT_FOUND, +}; +use std::ffi::{c_char, c_void, CStr, CString, NulError}; +use std::ptr::{null_mut, slice_from_raw_parts_mut, NonNull}; +use zerocopy::FromZeros; + +/// A mapping from string keys to values of various types. +#[derive(Debug)] +pub struct PersistableBundle(NonNull<APersistableBundle>); + +impl PersistableBundle { + /// Creates a new `PersistableBundle`. + pub fn new() -> Self { + // SAFETY: APersistableBundle_new doesn't actually have any safety requirements. + let bundle = unsafe { APersistableBundle_new() }; + Self(NonNull::new(bundle).expect("Allocated APersistableBundle was null")) + } + + /// Returns the number of mappings in the bundle. + pub fn size(&self) -> usize { + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. + unsafe { APersistableBundle_size(self.0.as_ptr()) } + .try_into() + .expect("APersistableBundle_size returned a negative size") + } + + /// Removes any entry with the given key. + /// + /// Returns an error if the given key contains a NUL character, otherwise returns whether there + /// was any entry to remove. + pub fn remove(&mut self, key: &str) -> Result<bool, NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + Ok(unsafe { APersistableBundle_erase(self.0.as_ptr(), key.as_ptr()) != 0 }) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_bool(&mut self, key: &str, value: bool) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putBoolean(self.0.as_ptr(), key.as_ptr(), value); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_int(&mut self, key: &str, value: i32) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putInt(self.0.as_ptr(), key.as_ptr(), value); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_long(&mut self, key: &str, value: i64) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putLong(self.0.as_ptr(), key.as_ptr(), value); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_double(&mut self, key: &str, value: f64) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putDouble(self.0.as_ptr(), key.as_ptr(), value); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key or value contains a NUL character. + pub fn insert_string(&mut self, key: &str, value: &str) -> Result<(), NulError> { + let key = CString::new(key)?; + let value = CString::new(value)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `CStr::as_ptr` is guaranteed + // to be valid for the duration of this call. + unsafe { + APersistableBundle_putString(self.0.as_ptr(), key.as_ptr(), value.as_ptr()); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_bool_vec(&mut self, key: &str, value: &[bool]) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putBooleanVector( + self.0.as_ptr(), + key.as_ptr(), + value.as_ptr(), + value.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_int_vec(&mut self, key: &str, value: &[i32]) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putIntVector( + self.0.as_ptr(), + key.as_ptr(), + value.as_ptr(), + value.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_long_vec(&mut self, key: &str, value: &[i64]) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putLongVector( + self.0.as_ptr(), + key.as_ptr(), + value.as_ptr(), + value.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_double_vec(&mut self, key: &str, value: &[f64]) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putDoubleVector( + self.0.as_ptr(), + key.as_ptr(), + value.as_ptr(), + value.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_string_vec<'a, T: ToString + 'a>( + &mut self, + key: &str, + value: impl IntoIterator<Item = &'a T>, + ) -> Result<(), NulError> { + let key = CString::new(key)?; + // We need to collect the new `CString`s into something first so that they live long enough + // for their pointers to be valid for the `APersistableBundle_putStringVector` call below. + let c_strings = value + .into_iter() + .map(|s| CString::new(s.to_string())) + .collect::<Result<Vec<_>, NulError>>()?; + let char_pointers = c_strings.iter().map(|s| s.as_ptr()).collect::<Vec<_>>(); + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call, and likewise the pointer returned by + // `value.as_ptr()` is guaranteed to be valid for at least `value.len()` values for the + // duration of the call. + unsafe { + APersistableBundle_putStringVector( + self.0.as_ptr(), + key.as_ptr(), + char_pointers.as_ptr(), + char_pointers.len().try_into().unwrap(), + ); + } + Ok(()) + } + + /// Inserts a key-value pair into the bundle. + /// + /// If the key is already present then its value will be overwritten by the given value. + /// + /// Returns an error if the key contains a NUL character. + pub fn insert_persistable_bundle( + &mut self, + key: &str, + value: &PersistableBundle, + ) -> Result<(), NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the + // lifetime of the `PersistableBundle`s. The pointer returned by `CStr::as_ptr` is + // guaranteed to be valid for the duration of this call, and + // `APersistableBundle_putPersistableBundle` does a deep copy so that is all that is + // required. + unsafe { + APersistableBundle_putPersistableBundle( + self.0.as_ptr(), + key.as_ptr(), + value.0.as_ptr(), + ); + } + Ok(()) + } + + /// Gets the boolean value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_bool(&self, key: &str) -> Result<Option<bool>, NulError> { + let key = CString::new(key)?; + let mut value = false; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. The value pointer must be valid because it + // comes from a reference. + if unsafe { APersistableBundle_getBoolean(self.0.as_ptr(), key.as_ptr(), &mut value) } { + Ok(Some(value)) + } else { + Ok(None) + } + } + + /// Gets the i32 value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_int(&self, key: &str) -> Result<Option<i32>, NulError> { + let key = CString::new(key)?; + let mut value = 0; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. The value pointer must be valid because it + // comes from a reference. + if unsafe { APersistableBundle_getInt(self.0.as_ptr(), key.as_ptr(), &mut value) } { + Ok(Some(value)) + } else { + Ok(None) + } + } + + /// Gets the i64 value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_long(&self, key: &str) -> Result<Option<i64>, NulError> { + let key = CString::new(key)?; + let mut value = 0; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. The value pointer must be valid because it + // comes from a reference. + if unsafe { APersistableBundle_getLong(self.0.as_ptr(), key.as_ptr(), &mut value) } { + Ok(Some(value)) + } else { + Ok(None) + } + } + + /// Gets the f64 value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_double(&self, key: &str) -> Result<Option<f64>, NulError> { + let key = CString::new(key)?; + let mut value = 0.0; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the duration of this call. The value pointer must be valid because it + // comes from a reference. + if unsafe { APersistableBundle_getDouble(self.0.as_ptr(), key.as_ptr(), &mut value) } { + Ok(Some(value)) + } else { + Ok(None) + } + } + + /// Gets the string value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_string(&self, key: &str) -> Result<Option<String>, NulError> { + let key = CString::new(key)?; + let mut value = null_mut(); + let mut allocated_size: usize = 0; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the lifetime of `key`. The value pointer must be valid because it comes + // from a reference. + let value_size_bytes = unsafe { + APersistableBundle_getString( + self.0.as_ptr(), + key.as_ptr(), + &mut value, + Some(string_allocator), + (&raw mut allocated_size).cast(), + ) + }; + match value_size_bytes { + APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None), + APERSISTABLEBUNDLE_ALLOCATOR_FAILED => { + panic!("APersistableBundle_getString failed to allocate string"); + } + _ => { + let raw_slice = slice_from_raw_parts_mut(value.cast(), allocated_size); + // SAFETY: The pointer was returned from string_allocator, which used + // `Box::into_raw`, and we've got the appropriate size back from allocated_size. + let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) }; + assert_eq!( + allocated_size, + usize::try_from(value_size_bytes) + .expect("APersistableBundle_getString returned negative value size") + + 1 + ); + let c_string = CString::from_vec_with_nul(boxed_slice.into()) + .expect("APersistableBundle_getString returned string missing NUL byte"); + let string = c_string + .into_string() + .expect("APersistableBundle_getString returned invalid UTF-8"); + Ok(Some(string)) + } + } + } + + /// Gets the vector of `T` associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + /// + /// `get_func` should be one of the `APersistableBundle_get*Vector` functions from + /// `binder_ndk_sys`. + /// + /// # Safety + /// + /// `get_func` must only require that the pointers it takes are valid for the duration of the + /// call. It must allow a null pointer for the buffer, and must return the size in bytes of + /// buffer it requires. If it is given a non-null buffer pointer it must write that number of + /// bytes to the buffer, which must be a whole number of valid `T` values. + unsafe fn get_vec<T: Clone>( + &self, + key: &str, + default: T, + get_func: unsafe extern "C" fn( + *const APersistableBundle, + *const c_char, + *mut T, + i32, + ) -> i32, + ) -> Result<Option<Vec<T>>, NulError> { + let key = CString::new(key)?; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the lifetime of `key`. A null pointer is allowed for the buffer. + match unsafe { get_func(self.0.as_ptr(), key.as_ptr(), null_mut(), 0) } { + APERSISTABLEBUNDLE_KEY_NOT_FOUND => Ok(None), + APERSISTABLEBUNDLE_ALLOCATOR_FAILED => { + panic!("APersistableBundle_getStringVector failed to allocate string"); + } + required_buffer_size => { + let mut value = vec![ + default; + usize::try_from(required_buffer_size).expect( + "APersistableBundle_get*Vector returned invalid size" + ) / size_of::<T>() + ]; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for + // the lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` + // is guaranteed to be valid for the lifetime of `key`. The value buffer pointer is + // valid as it comes from the Vec we just allocated. + match unsafe { + get_func( + self.0.as_ptr(), + key.as_ptr(), + value.as_mut_ptr(), + (value.len() * size_of::<T>()).try_into().unwrap(), + ) + } { + APERSISTABLEBUNDLE_KEY_NOT_FOUND => { + panic!("APersistableBundle_get*Vector failed to find key after first finding it"); + } + APERSISTABLEBUNDLE_ALLOCATOR_FAILED => { + panic!("APersistableBundle_getStringVector failed to allocate string"); + } + _ => Ok(Some(value)), + } + } + } + } + + /// Gets the boolean vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_bool_vec(&self, key: &str) -> Result<Option<Vec<bool>>, NulError> { + // SAFETY: APersistableBundle_getBooleanVector fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, Default::default(), APersistableBundle_getBooleanVector) } + } + + /// Gets the i32 vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_int_vec(&self, key: &str) -> Result<Option<Vec<i32>>, NulError> { + // SAFETY: APersistableBundle_getIntVector fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, Default::default(), APersistableBundle_getIntVector) } + } + + /// Gets the i64 vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_long_vec(&self, key: &str) -> Result<Option<Vec<i64>>, NulError> { + // SAFETY: APersistableBundle_getLongVector fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, Default::default(), APersistableBundle_getLongVector) } + } + + /// Gets the f64 vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_double_vec(&self, key: &str) -> Result<Option<Vec<f64>>, NulError> { + // SAFETY: APersistableBundle_getDoubleVector fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, Default::default(), APersistableBundle_getDoubleVector) } + } + + /// Gets the string vector value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_string_vec(&self, key: &str) -> Result<Option<Vec<String>>, NulError> { + if let Some(value) = + // SAFETY: `get_string_vector_with_allocator` fulfils all the safety requirements of + // `get_vec`. + unsafe { self.get_vec(key, null_mut(), get_string_vector_with_allocator) }? + { + Ok(Some( + value + .into_iter() + .map(|s| { + // SAFETY: The pointer was returned from `string_allocator`, which used + // `Box::into_raw`, and `APersistableBundle_getStringVector` should have + // written valid bytes to it including a NUL terminator in the last + // position. + let string_length = unsafe { CStr::from_ptr(s) }.count_bytes(); + let raw_slice = slice_from_raw_parts_mut(s.cast(), string_length + 1); + // SAFETY: The pointer was returned from `string_allocator`, which used + // `Box::into_raw`, and we've got the appropriate size back by checking the + // length of the string. + let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) }; + let c_string = CString::from_vec_with_nul(boxed_slice.into()).expect( + "APersistableBundle_getStringVector returned string missing NUL byte", + ); + c_string + .into_string() + .expect("APersistableBundle_getStringVector returned invalid UTF-8") + }) + .collect(), + )) + } else { + Ok(None) + } + } + + /// Gets the `PersistableBundle` value associated with the given key. + /// + /// Returns an error if the key contains a NUL character, or `Ok(None)` if the key doesn't exist + /// in the bundle. + pub fn get_persistable_bundle(&self, key: &str) -> Result<Option<Self>, NulError> { + let key = CString::new(key)?; + let mut value = null_mut(); + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. The pointer returned by `key.as_ptr()` is guaranteed + // to be valid for the lifetime of `key`. The value pointer must be valid because it comes + // from a reference. + if unsafe { + APersistableBundle_getPersistableBundle(self.0.as_ptr(), key.as_ptr(), &mut value) + } { + Ok(Some(Self(NonNull::new(value).expect( + "APersistableBundle_getPersistableBundle returned true but didn't set outBundle", + )))) + } else { + Ok(None) + } + } + + /// Calls the appropriate `APersistableBundle_get*Keys` function for the given `value_type`, + /// with our `string_allocator` and a null context pointer. + /// + /// # Safety + /// + /// `out_keys` must either be null or point to a buffer of at least `buffer_size_bytes` bytes, + /// properly aligned for `T`, and not otherwise accessed for the duration of the call. + unsafe fn get_keys_raw( + &self, + value_type: ValueType, + out_keys: *mut *mut c_char, + buffer_size_bytes: i32, + ) -> i32 { + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. Our caller guarantees an appropriate value for + // `out_keys` and `buffer_size_bytes`. + unsafe { + match value_type { + ValueType::Boolean => APersistableBundle_getBooleanKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::Integer => APersistableBundle_getIntKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::Long => APersistableBundle_getLongKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::Double => APersistableBundle_getDoubleKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::String => APersistableBundle_getStringKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::BooleanVector => APersistableBundle_getBooleanVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::IntegerVector => APersistableBundle_getIntVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::LongVector => APersistableBundle_getLongVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::DoubleVector => APersistableBundle_getDoubleVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::StringVector => APersistableBundle_getStringVectorKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + ValueType::PersistableBundle => APersistableBundle_getPersistableBundleKeys( + self.0.as_ptr(), + out_keys, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ), + } + } + } + + /// Gets all the keys associated with values of the given type. + pub fn keys_for_type(&self, value_type: ValueType) -> Vec<String> { + // SAFETY: A null pointer is allowed for the buffer. + match unsafe { self.get_keys_raw(value_type, null_mut(), 0) } { + APERSISTABLEBUNDLE_ALLOCATOR_FAILED => { + panic!("APersistableBundle_get*Keys failed to allocate string"); + } + required_buffer_size => { + let required_buffer_size_usize = usize::try_from(required_buffer_size) + .expect("APersistableBundle_get*Keys returned invalid size"); + assert_eq!(required_buffer_size_usize % size_of::<*mut c_char>(), 0); + let mut keys = + vec![null_mut(); required_buffer_size_usize / size_of::<*mut c_char>()]; + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for + // the lifetime of the `PersistableBundle`. The keys buffer pointer is valid as it + // comes from the Vec we just allocated. + if unsafe { self.get_keys_raw(value_type, keys.as_mut_ptr(), required_buffer_size) } + == APERSISTABLEBUNDLE_ALLOCATOR_FAILED + { + panic!("APersistableBundle_get*Keys failed to allocate string"); + } + keys.into_iter() + .map(|key| { + // SAFETY: The pointer was returned from `string_allocator`, which used + // `Box::into_raw`, and `APersistableBundle_getStringVector` should have + // written valid bytes to it including a NUL terminator in the last + // position. + let string_length = unsafe { CStr::from_ptr(key) }.count_bytes(); + let raw_slice = slice_from_raw_parts_mut(key.cast(), string_length + 1); + // SAFETY: The pointer was returned from `string_allocator`, which used + // `Box::into_raw`, and we've got the appropriate size back by checking the + // length of the string. + let boxed_slice: Box<[u8]> = unsafe { Box::from_raw(raw_slice) }; + let c_string = CString::from_vec_with_nul(boxed_slice.into()) + .expect("APersistableBundle_get*Keys returned string missing NUL byte"); + c_string + .into_string() + .expect("APersistableBundle_get*Keys returned invalid UTF-8") + }) + .collect() + } + } + } + + /// Returns an iterator over all keys in the bundle, along with the type of their associated + /// value. + pub fn keys(&self) -> impl Iterator<Item = (String, ValueType)> + use<'_> { + [ + ValueType::Boolean, + ValueType::Integer, + ValueType::Long, + ValueType::Double, + ValueType::String, + ValueType::BooleanVector, + ValueType::IntegerVector, + ValueType::LongVector, + ValueType::DoubleVector, + ValueType::StringVector, + ValueType::PersistableBundle, + ] + .iter() + .flat_map(|value_type| { + self.keys_for_type(*value_type).into_iter().map(|key| (key, *value_type)) + }) + } +} + +/// Wrapper around `APersistableBundle_getStringVector` to pass `string_allocator` and a null +/// context pointer. +/// +/// # Safety +/// +/// * `bundle` must point to a valid `APersistableBundle` which is not modified for the duration of +/// the call. +/// * `key` must point to a valid NUL-terminated C string. +/// * `buffer` must either be null or point to a buffer of at least `buffer_size_bytes` bytes, +/// properly aligned for `T`, and not otherwise accessed for the duration of the call. +unsafe extern "C" fn get_string_vector_with_allocator( + bundle: *const APersistableBundle, + key: *const c_char, + buffer: *mut *mut c_char, + buffer_size_bytes: i32, +) -> i32 { + // SAFETY: The safety requirements are all guaranteed by our caller according to the safety + // documentation above. + unsafe { + APersistableBundle_getStringVector( + bundle, + key, + buffer, + buffer_size_bytes, + Some(string_allocator), + null_mut(), + ) + } +} + +// SAFETY: The underlying *APersistableBundle can be moved between threads. +unsafe impl Send for PersistableBundle {} + +// SAFETY: The underlying *APersistableBundle can be read from multiple threads, and we require +// `&mut PersistableBundle` for any operations which mutate it. +unsafe impl Sync for PersistableBundle {} + +impl Default for PersistableBundle { + fn default() -> Self { + Self::new() + } +} + +impl Drop for PersistableBundle { + fn drop(&mut self) { + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of this `PersistableBundle`. + unsafe { APersistableBundle_delete(self.0.as_ptr()) }; + } +} + +impl Clone for PersistableBundle { + fn clone(&self) -> Self { + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. + let duplicate = unsafe { APersistableBundle_dup(self.0.as_ptr()) }; + Self(NonNull::new(duplicate).expect("Duplicated APersistableBundle was null")) + } +} + +impl PartialEq for PersistableBundle { + fn eq(&self, other: &Self) -> bool { + // SAFETY: The wrapped `APersistableBundle` pointers are guaranteed to be valid for the + // lifetime of the `PersistableBundle`s. + unsafe { APersistableBundle_isEqual(self.0.as_ptr(), other.0.as_ptr()) } + } +} + +impl UnstructuredParcelable for PersistableBundle { + fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> { + let status = + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. `parcel.as_native_mut()` always returns a valid + // parcel pointer. + unsafe { APersistableBundle_writeToParcel(self.0.as_ptr(), parcel.as_native_mut()) }; + status_result(status) + } + + fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> { + let mut bundle = null_mut(); + + // SAFETY: The wrapped `APersistableBundle` pointer is guaranteed to be valid for the + // lifetime of the `PersistableBundle`. `parcel.as_native()` always returns a valid parcel + // pointer. + let status = unsafe { APersistableBundle_readFromParcel(parcel.as_native(), &mut bundle) }; + status_result(status)?; + + Ok(Self(NonNull::new(bundle).expect( + "APersistableBundle_readFromParcel returned success but didn't allocate bundle", + ))) + } +} + +/// Allocates a boxed slice of the given size in bytes, returns a pointer to it and writes its size +/// to `*context`. +/// +/// # Safety +/// +/// `context` must either be null or point to a `usize` to which we can write. +unsafe extern "C" fn string_allocator(size: i32, context: *mut c_void) -> *mut c_char { + let Ok(size) = size.try_into() else { + return null_mut(); + }; + let Ok(boxed_slice) = <[c_char]>::new_box_zeroed_with_elems(size) else { + return null_mut(); + }; + if !context.is_null() { + // SAFETY: The caller promised that `context` is either null or points to a `usize` to which + // we can write, and we just checked that it's not null. + unsafe { + *context.cast::<usize>() = size; + } + } + Box::into_raw(boxed_slice).cast() +} + +impl_deserialize_for_unstructured_parcelable!(PersistableBundle); +impl_serialize_for_unstructured_parcelable!(PersistableBundle); + +/// The types which may be stored as values in a [`PersistableBundle`]. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ValueType { + /// A `bool`. + Boolean, + /// An `i32`. + Integer, + /// An `i64`. + Long, + /// An `f64`. + Double, + /// A string. + String, + /// A vector of `bool`s. + BooleanVector, + /// A vector of `i32`s. + IntegerVector, + /// A vector of `i64`s. + LongVector, + /// A vector of `f64`s. + DoubleVector, + /// A vector of strings. + StringVector, + /// A nested `PersistableBundle`. + PersistableBundle, +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn create_delete() { + let bundle = PersistableBundle::new(); + drop(bundle); + } + + #[test] + fn duplicate_equal() { + let bundle = PersistableBundle::new(); + let duplicate = bundle.clone(); + assert_eq!(bundle, duplicate); + } + + #[test] + fn get_empty() { + let bundle = PersistableBundle::new(); + assert_eq!(bundle.get_bool("foo"), Ok(None)); + assert_eq!(bundle.get_int("foo"), Ok(None)); + assert_eq!(bundle.get_long("foo"), Ok(None)); + assert_eq!(bundle.get_double("foo"), Ok(None)); + assert_eq!(bundle.get_bool_vec("foo"), Ok(None)); + assert_eq!(bundle.get_int_vec("foo"), Ok(None)); + assert_eq!(bundle.get_long_vec("foo"), Ok(None)); + assert_eq!(bundle.get_double_vec("foo"), Ok(None)); + assert_eq!(bundle.get_string("foo"), Ok(None)); + } + + #[test] + fn remove_empty() { + let mut bundle = PersistableBundle::new(); + assert_eq!(bundle.remove("foo"), Ok(false)); + } + + #[test] + fn insert_get_primitives() { + let mut bundle = PersistableBundle::new(); + + assert_eq!(bundle.insert_bool("bool", true), Ok(())); + assert_eq!(bundle.insert_int("int", 42), Ok(())); + assert_eq!(bundle.insert_long("long", 66), Ok(())); + assert_eq!(bundle.insert_double("double", 123.4), Ok(())); + + assert_eq!(bundle.get_bool("bool"), Ok(Some(true))); + assert_eq!(bundle.get_int("int"), Ok(Some(42))); + assert_eq!(bundle.get_long("long"), Ok(Some(66))); + assert_eq!(bundle.get_double("double"), Ok(Some(123.4))); + assert_eq!(bundle.size(), 4); + + // Getting the wrong type should return nothing. + assert_eq!(bundle.get_int("bool"), Ok(None)); + assert_eq!(bundle.get_long("bool"), Ok(None)); + assert_eq!(bundle.get_double("bool"), Ok(None)); + assert_eq!(bundle.get_bool("int"), Ok(None)); + assert_eq!(bundle.get_long("int"), Ok(None)); + assert_eq!(bundle.get_double("int"), Ok(None)); + assert_eq!(bundle.get_bool("long"), Ok(None)); + assert_eq!(bundle.get_int("long"), Ok(None)); + assert_eq!(bundle.get_double("long"), Ok(None)); + assert_eq!(bundle.get_bool("double"), Ok(None)); + assert_eq!(bundle.get_int("double"), Ok(None)); + assert_eq!(bundle.get_long("double"), Ok(None)); + + // If they are removed they should no longer be present. + assert_eq!(bundle.remove("bool"), Ok(true)); + assert_eq!(bundle.remove("int"), Ok(true)); + assert_eq!(bundle.remove("long"), Ok(true)); + assert_eq!(bundle.remove("double"), Ok(true)); + assert_eq!(bundle.get_bool("bool"), Ok(None)); + assert_eq!(bundle.get_int("int"), Ok(None)); + assert_eq!(bundle.get_long("long"), Ok(None)); + assert_eq!(bundle.get_double("double"), Ok(None)); + assert_eq!(bundle.size(), 0); + } + + #[test] + fn insert_get_string() { + let mut bundle = PersistableBundle::new(); + + assert_eq!(bundle.insert_string("string", "foo"), Ok(())); + assert_eq!(bundle.insert_string("empty", ""), Ok(())); + assert_eq!(bundle.size(), 2); + + assert_eq!(bundle.get_string("string"), Ok(Some("foo".to_string()))); + assert_eq!(bundle.get_string("empty"), Ok(Some("".to_string()))); + } + + #[test] + fn insert_get_vec() { + let mut bundle = PersistableBundle::new(); + + assert_eq!(bundle.insert_bool_vec("bool", &[]), Ok(())); + assert_eq!(bundle.insert_int_vec("int", &[42]), Ok(())); + assert_eq!(bundle.insert_long_vec("long", &[66, 67, 68]), Ok(())); + assert_eq!(bundle.insert_double_vec("double", &[123.4]), Ok(())); + assert_eq!(bundle.insert_string_vec("string", &["foo", "bar", "baz"]), Ok(())); + assert_eq!( + bundle.insert_string_vec( + "string", + &[&"foo".to_string(), &"bar".to_string(), &"baz".to_string()] + ), + Ok(()) + ); + assert_eq!( + bundle.insert_string_vec( + "string", + &["foo".to_string(), "bar".to_string(), "baz".to_string()] + ), + Ok(()) + ); + + assert_eq!(bundle.size(), 5); + + assert_eq!(bundle.get_bool_vec("bool"), Ok(Some(vec![]))); + assert_eq!(bundle.get_int_vec("int"), Ok(Some(vec![42]))); + assert_eq!(bundle.get_long_vec("long"), Ok(Some(vec![66, 67, 68]))); + assert_eq!(bundle.get_double_vec("double"), Ok(Some(vec![123.4]))); + assert_eq!( + bundle.get_string_vec("string"), + Ok(Some(vec!["foo".to_string(), "bar".to_string(), "baz".to_string()])) + ); + } + + #[test] + fn insert_get_bundle() { + let mut bundle = PersistableBundle::new(); + + let mut sub_bundle = PersistableBundle::new(); + assert_eq!(sub_bundle.insert_int("int", 42), Ok(())); + assert_eq!(sub_bundle.size(), 1); + assert_eq!(bundle.insert_persistable_bundle("bundle", &sub_bundle), Ok(())); + + assert_eq!(bundle.get_persistable_bundle("bundle"), Ok(Some(sub_bundle))); + } + + #[test] + fn get_keys() { + let mut bundle = PersistableBundle::new(); + + assert_eq!(bundle.keys_for_type(ValueType::Boolean), Vec::<String>::new()); + assert_eq!(bundle.keys_for_type(ValueType::Integer), Vec::<String>::new()); + assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new()); + + assert_eq!(bundle.insert_bool("bool1", false), Ok(())); + assert_eq!(bundle.insert_bool("bool2", true), Ok(())); + assert_eq!(bundle.insert_int("int", 42), Ok(())); + + assert_eq!( + bundle.keys_for_type(ValueType::Boolean), + vec!["bool1".to_string(), "bool2".to_string()] + ); + assert_eq!(bundle.keys_for_type(ValueType::Integer), vec!["int".to_string()]); + assert_eq!(bundle.keys_for_type(ValueType::StringVector), Vec::<String>::new()); + + assert_eq!( + bundle.keys().collect::<Vec<_>>(), + vec![ + ("bool1".to_string(), ValueType::Boolean), + ("bool2".to_string(), ValueType::Boolean), + ("int".to_string(), ValueType::Integer), + ] + ); + } +} diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp index 557f0e895d..c19e375a97 100644 --- a/libs/binder/rust/sys/BinderBindings.hpp +++ b/libs/binder/rust/sys/BinderBindings.hpp @@ -17,6 +17,7 @@ #include <android/binder_ibinder.h> #include <android/binder_parcel.h> #include <android/binder_status.h> +#include <android/persistable_bundle.h> /* Platform only */ #if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__) @@ -91,6 +92,11 @@ enum { #endif }; +enum { + APERSISTABLEBUNDLE_KEY_NOT_FOUND = APERSISTABLEBUNDLE_KEY_NOT_FOUND, + APERSISTABLEBUNDLE_ALLOCATOR_FAILED = APERSISTABLEBUNDLE_ALLOCATOR_FAILED, +}; + } // namespace consts } // namespace c_interface diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl index 116476765a..dcd6461b53 100644 --- a/libs/binder/tests/IBinderRpcTest.aidl +++ b/libs/binder/tests/IBinderRpcTest.aidl @@ -34,6 +34,8 @@ interface IBinderRpcTest { void holdBinder(@nullable IBinder binder); @nullable IBinder getHeldBinder(); + byte[] repeatBytes(in byte[] bytes); + // Idea is client creates its own instance of IBinderRpcTest and calls this, // and the server calls 'binder' with (calls - 1) passing itself as 'binder', // going back and forth until calls = 0 diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 9f656ec96c..e88e3f3530 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -711,6 +711,35 @@ TEST_P(BinderRpc, OnewayCallExhaustion) { proc.proc->sessions.erase(proc.proc->sessions.begin() + 1); } +// TODO(b/392717039): can we move this to universal tests? +TEST_P(BinderRpc, SendTooLargeVector) { + if (GetParam().singleThreaded) { + GTEST_SKIP() << "Requires multi-threaded server to test one of the sessions crashing."; + } + + auto proc = createRpcTestSocketServerProcess({.numSessions = 2}); + + // need a working transaction + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + // see libbinder internal Constants.h + const size_t kTooLargeSize = 650 * 1024; + const std::vector<uint8_t> kTestValue(kTooLargeSize / sizeof(uint8_t), 42); + + // TODO(b/392717039): Telling a server to allocate too much data currently causes the session to + // close since RpcServer treats any transaction error as a failure. We likely want to change + // this behavior to be a soft failure, since it isn't hard to keep track of this state. + sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root); + std::vector<uint8_t> result; + status_t res = rootIface2->repeatBytes(kTestValue, &result).transactionError(); + + // TODO(b/392717039): consistent error results always + EXPECT_TRUE(res == -ECONNRESET || res == DEAD_OBJECT) << statusToString(res); + + // died, so remove it for checks in destructor of proc + proc.proc->sessions.erase(proc.proc->sessions.begin() + 1); +} + TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) { if (clientOrServerSingleThreaded()) { GTEST_SKIP() << "This test requires multiple threads"; diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index dc22647b85..6e0024628a 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -348,6 +348,10 @@ public: *out = binder; return Status::ok(); } + Status repeatBytes(const std::vector<uint8_t>& bytes, std::vector<uint8_t>* out) override { + *out = bytes; + return Status::ok(); + } static sp<IBinder> mHeldBinder; Status holdBinder(const sp<IBinder>& binder) override { mHeldBinder = binder; diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp index c6fd4870a0..d227e6eeec 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -209,6 +209,18 @@ TEST_P(BinderRpc, RepeatBinder) { EXPECT_EQ(0, MyBinderRpcSession::gNum); } +TEST_P(BinderRpc, SendLargeVector) { + auto proc = createRpcTestSocketServerProcess({}); + + // see libbinder internal Constants.h + const size_t kLargeSize = 550 * 1024; + const std::vector<uint8_t> kTestValue(kLargeSize / sizeof(uint8_t), 42); + + std::vector<uint8_t> result; + EXPECT_OK(proc.rootIface->repeatBytes(kTestValue, &result)); + EXPECT_EQ(result, kTestValue); +} + TEST_P(BinderRpc, RepeatTheirBinder) { auto proc = createRpcTestSocketServerProcess({}); @@ -498,9 +510,9 @@ TEST_P(BinderRpc, Callbacks) { // 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; + using std::literals::chrono_literals::operator""ms; RpcMutexUniqueLock _l(cb->mMutex); - cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); + cb->mCv.wait_for(_l, 1500ms, [&] { return !cb->mValues.empty(); }); } EXPECT_EQ(cb->mValues.size(), 1UL) diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp index c74ba0a65e..65ad896026 100644 --- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -47,6 +47,71 @@ public: return mHaveMessage ? OK : WOULD_BLOCK; } + void moveMsgStart(ipc_msg_t* msg, size_t msg_size, size_t offset) { + LOG_ALWAYS_FATAL_IF(offset > msg_size, "tried to move message past its end %zd>%zd", offset, + msg_size); + while (true) { + if (offset == 0) { + break; + } + if (offset >= msg->iov[0].iov_len) { + // Move to the next iov, this one was sent already + offset -= msg->iov[0].iov_len; + msg->iov++; + msg->num_iov -= 1; + } else { + // We need to move the base of the current iov + msg->iov[0].iov_len -= offset; + msg->iov[0].iov_base = static_cast<char*>(msg->iov[0].iov_base) + offset; + offset = 0; + } + } + // We only send handles on the first message. This can be changed in the future if we want + // to send more handles than the maximum per message limit (which would require sending + // multiple messages). The current code makes sure that we send less handles than the + // maximum trusty allows. + msg->num_handles = 0; + } + + status_t sendTrustyMsg(ipc_msg_t* msg, size_t msg_size) { + do { + ssize_t rc = send_msg(mSocket.fd.get(), msg); + if (rc == ERR_NOT_ENOUGH_BUFFER) { + // Peer is blocked, wait until it unblocks. + // TODO: when tipc supports a send-unblocked handler, + // save the message here in a queue and retry it asynchronously + // when the handler gets called by the library + uevent uevt; + do { + rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME); + if (rc < 0) { + return statusFromTrusty(rc); + } + if (uevt.event & IPC_HANDLE_POLL_HUP) { + return DEAD_OBJECT; + } + } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED)); + + // Retry the send, it should go through this time because + // sending is now unblocked + rc = send_msg(mSocket.fd.get(), msg); + } + if (rc < 0) { + return statusFromTrusty(rc); + } + size_t sent_bytes = static_cast<size_t>(rc); + if (sent_bytes < msg_size) { + moveMsgStart(msg, msg_size, static_cast<size_t>(sent_bytes)); + msg_size -= sent_bytes; + } else { + LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != msg_size, + "Sent the wrong number of bytes %zd!=%zu", rc, msg_size); + break; + } + } while (true); + return OK; + } + status_t interruptableWriteFully( FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, const std::optional<SmallFunction<status_t()>>& /*altPoll*/, @@ -86,34 +151,7 @@ public: msg.handles = msgHandles; } - ssize_t rc = send_msg(mSocket.fd.get(), &msg); - if (rc == ERR_NOT_ENOUGH_BUFFER) { - // Peer is blocked, wait until it unblocks. - // TODO: when tipc supports a send-unblocked handler, - // save the message here in a queue and retry it asynchronously - // when the handler gets called by the library - uevent uevt; - do { - rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME); - if (rc < 0) { - return statusFromTrusty(rc); - } - if (uevt.event & IPC_HANDLE_POLL_HUP) { - return DEAD_OBJECT; - } - } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED)); - - // Retry the send, it should go through this time because - // sending is now unblocked - rc = send_msg(mSocket.fd.get(), &msg); - } - if (rc < 0) { - return statusFromTrusty(rc); - } - LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size, - "Sent the wrong number of bytes %zd!=%zu", rc, size); - - return OK; + return sendTrustyMsg(&msg, size); } status_t interruptableReadFully( diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json index 6e20b8a7ad..da0f2ed2d8 100644 --- a/libs/binder/trusty/binderRpcTest/manifest.json +++ b/libs/binder/trusty/binderRpcTest/manifest.json @@ -1,6 +1,6 @@ { "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b", "app_name": "binderRpcTest", - "min_heap": 262144, + "min_heap": 4194304, "min_stack": 20480 } diff --git a/libs/binder/trusty/binderRpcTest/service/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json index d2a1fc0a48..55ff49c0b8 100644 --- a/libs/binder/trusty/binderRpcTest/service/manifest.json +++ b/libs/binder/trusty/binderRpcTest/service/manifest.json @@ -1,7 +1,7 @@ { "uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94", "app_name": "binderRpcTestService", - "min_heap": 65536, + "min_heap": 4194304, "min_stack": 20480, "mgmt_flags": { "restart_on_exit": true, diff --git a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs index 22cba44975..caf3117195 100644 --- a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs +++ b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs @@ -82,6 +82,9 @@ impl IBinderRpcTest for MyBinderRpcSession { fn repeatBinder(&self, _binder: Option<&SpIBinder>) -> Result<Option<SpIBinder>, Status> { todo!() } + fn repeatBytes(&self, _bytes: &[u8]) -> Result<Vec<u8>, Status> { + todo!() + } fn holdBinder(&self, _binder: Option<&SpIBinder>) -> Result<(), Status> { todo!() } diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs index c4a758a214..6f454be2b6 100644 --- a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs +++ b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs @@ -96,6 +96,9 @@ impl IBinderRpcTest for TestService { None => Err(Status::from(StatusCode::BAD_VALUE)), } } + fn repeatBytes(&self, _bytes: &[u8]) -> Result<Vec<u8>, Status> { + todo!() + } fn holdBinder(&self, binder: Option<&SpIBinder>) -> Result<(), Status> { *HOLD_BINDER.lock().unwrap() = binder.cloned(); Ok(()) diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs index 6bf79d4e57..a8acdedc55 100644 --- a/libs/debugstore/rust/src/core.rs +++ b/libs/debugstore/rust/src/core.rs @@ -48,7 +48,7 @@ impl DebugStore { /// /// This constant is used as a part of the debug store's data format, /// allowing for version tracking and compatibility checks. - const ENCODE_VERSION: u32 = 1; + const ENCODE_VERSION: u32 = 2; /// Creates a new instance of `DebugStore` with specified event limit and maximum delay. fn new() -> Self { @@ -129,7 +129,7 @@ impl fmt::Display for DebugStore { write!( f, "{}", - self.event_store.fold(String::new(), |mut acc, event| { + self.event_store.rfold(String::new(), |mut acc, event| { if !acc.is_empty() { acc.push_str("||"); } diff --git a/libs/debugstore/rust/src/storage.rs b/libs/debugstore/rust/src/storage.rs index 2ad7f4e0b4..47760f3f21 100644 --- a/libs/debugstore/rust/src/storage.rs +++ b/libs/debugstore/rust/src/storage.rs @@ -32,14 +32,18 @@ impl<T, const N: usize> Storage<T, N> { self.insertion_buffer.force_push(value); } - /// Folds over the elements in the storage using the provided function. - pub fn fold<U, F>(&self, init: U, mut func: F) -> U + /// Folds over the elements in the storage in reverse order using the provided function. + pub fn rfold<U, F>(&self, init: U, mut func: F) -> U where F: FnMut(U, &T) -> U, { - let mut acc = init; + let mut items = Vec::new(); while let Some(value) = self.insertion_buffer.pop() { - acc = func(acc, &value); + items.push(value); + } + let mut acc = init; + for value in items.iter().rev() { + acc = func(acc, value); } acc } @@ -59,18 +63,18 @@ mod tests { let storage = Storage::<i32, 10>::new(); storage.insert(7); - let sum = storage.fold(0, |acc, &x| acc + x); + let sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!(sum, 7, "The sum of the elements should be equal to the inserted value."); } #[test] - fn test_fold_functionality() { + fn test_rfold_functionality() { let storage = Storage::<i32, 5>::new(); storage.insert(1); storage.insert(2); storage.insert(3); - let sum = storage.fold(0, |acc, &x| acc + x); + let sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!( sum, 6, "The sum of the elements should be equal to the sum of inserted values." @@ -84,13 +88,13 @@ mod tests { storage.insert(2); storage.insert(5); - let first_sum = storage.fold(0, |acc, &x| acc + x); + let first_sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!(first_sum, 8, "The sum of the elements should be equal to the inserted values."); storage.insert(30); storage.insert(22); - let second_sum = storage.fold(0, |acc, &x| acc + x); + let second_sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!( second_sum, 52, "The sum of the elements should be equal to the inserted values." @@ -103,7 +107,7 @@ mod tests { storage.insert(1); // This value should overwrite the previously inserted value (1). storage.insert(4); - let sum = storage.fold(0, |acc, &x| acc + x); + let sum = storage.rfold(0, |acc, &x| acc + x); assert_eq!(sum, 4, "The sum of the elements should be equal to the inserted values."); } @@ -128,7 +132,24 @@ mod tests { thread.join().expect("Thread should finish without panicking"); } - let count = storage.fold(0, |acc, _| acc + 1); + let count = storage.rfold(0, |acc, _| acc + 1); assert_eq!(count, 100, "Storage should be filled to its limit with concurrent insertions."); } + + #[test] + fn test_rfold_order() { + let storage = Storage::<i32, 5>::new(); + storage.insert(1); + storage.insert(2); + storage.insert(3); + + let mut result = Vec::new(); + storage.rfold((), |_, &x| result.push(x)); + + assert_eq!( + result, + vec![3, 2, 1], + "Elements should be processed in reverse order of insertion" + ); + } } diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index af50a2980c..dce7778229 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -21,10 +21,27 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "graphicsenv_flags", + package: "com.android.graphics.graphicsenv.flags", + container: "system", + srcs: ["graphicsenv_flags.aconfig"], +} + +cc_aconfig_library { + name: "graphicsenv_flags_c_lib", + aconfig_declarations: "graphicsenv_flags", +} + cc_library_shared { name: "libgraphicsenv", + defaults: [ + "aconfig_lib_cc_static_link.defaults", + ], + srcs: [ + "FeatureOverrides.cpp", "GpuStatsInfo.cpp", "GraphicsEnv.cpp", "IGpuService.cpp", @@ -35,6 +52,10 @@ cc_library_shared { "-Werror", ], + static_libs: [ + "graphicsenv_flags_c_lib", + ], + shared_libs: [ "libbase", "libbinder", @@ -42,6 +63,7 @@ cc_library_shared { "libdl_android", "liblog", "libutils", + "server_configurable_flags", ], header_libs: [ diff --git a/libs/graphicsenv/FeatureOverrides.cpp b/libs/graphicsenv/FeatureOverrides.cpp new file mode 100644 index 0000000000..6974da9934 --- /dev/null +++ b/libs/graphicsenv/FeatureOverrides.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2025 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 <graphicsenv/FeatureOverrides.h> + +#include <android-base/stringprintf.h> + +namespace android { + +using base::StringAppendF; + +std::string FeatureConfig::toString() const { + std::string result; + StringAppendF(&result, "Feature: %s\n", mFeatureName.c_str()); + StringAppendF(&result, " Status: %s\n", mEnabled ? "enabled" : "disabled"); + + return result; +} + +std::string FeatureOverrides::toString() const { + std::string result; + result.append("Global Features:\n"); + for (auto& cfg : mGlobalFeatures) { + result.append(" " + cfg.toString()); + } + result.append("\n"); + result.append("Package Features:\n"); + for (const auto& packageFeature : mPackageFeatures) { + result.append(" Package:"); + StringAppendF(&result, " %s\n", packageFeature.first.c_str()); + for (auto& cfg : packageFeature.second) { + result.append(" " + cfg.toString()); + } + } + + return result; +} + +} // namespace android diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 4874dbde9c..4bc261106a 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -29,6 +29,7 @@ #include <android-base/strings.h> #include <android/dlext.h> #include <binder/IServiceManager.h> +#include <com_android_graphics_graphicsenv_flags.h> #include <graphicsenv/IGpuService.h> #include <log/log.h> #include <nativeloader/dlext_namespaces.h> @@ -70,6 +71,8 @@ static bool isVndkEnabled() { } } // namespace +namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags; + namespace android { enum NativeLibrary { @@ -624,10 +627,36 @@ std::string& GraphicsEnv::getPackageName() { return mPackageName; } +// List of ANGLE features to enable, specified in the Global.Settings value "angle_egl_features". const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() { return mAngleEglFeatures; } +void GraphicsEnv::getAngleFeatureOverrides(std::vector<const char*>& enabled, + std::vector<const char*>& disabled) { + if (!graphicsenv_flags::feature_overrides()) { + return; + } + + for (const FeatureConfig& feature : mFeatureOverrides.mGlobalFeatures) { + if (feature.mEnabled) { + enabled.push_back(feature.mFeatureName.c_str()); + } else { + disabled.push_back(feature.mFeatureName.c_str()); + } + } + + if (mFeatureOverrides.mPackageFeatures.count(mPackageName)) { + for (const FeatureConfig& feature : mFeatureOverrides.mPackageFeatures[mPackageName]) { + if (feature.mEnabled) { + enabled.push_back(feature.mFeatureName.c_str()); + } else { + disabled.push_back(feature.mFeatureName.c_str()); + } + } + } +} + android_namespace_t* GraphicsEnv::getAngleNamespace() { std::lock_guard<std::mutex> lock(mNamespaceMutex); diff --git a/libs/graphicsenv/graphicsenv_flags.aconfig b/libs/graphicsenv/graphicsenv_flags.aconfig new file mode 100644 index 0000000000..ac66362242 --- /dev/null +++ b/libs/graphicsenv/graphicsenv_flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.graphics.graphicsenv.flags" +container: "system" + +flag { + name: "feature_overrides" + namespace: "core_graphics" + description: "This flag controls the Feature Overrides in GraphicsEnv." + bug: "372694741" +} diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h new file mode 100644 index 0000000000..2b9418733d --- /dev/null +++ b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h @@ -0,0 +1,52 @@ +/* + * Copyright 2024 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 <map> +#include <string> +#include <vector> + +namespace android { + +class FeatureConfig { +public: + FeatureConfig() = default; + FeatureConfig(const FeatureConfig&) = default; + virtual ~FeatureConfig() = default; + std::string toString() const; + + std::string mFeatureName; + bool mEnabled; +}; + +/* + * Class for transporting OpenGL ES Feature configurations from GpuService to authorized + * recipients. + */ +class FeatureOverrides { +public: + FeatureOverrides() = default; + FeatureOverrides(const FeatureOverrides&) = default; + virtual ~FeatureOverrides() = default; + std::string toString() const; + + std::vector<FeatureConfig> mGlobalFeatures; + /* Key: Package Name, Value: Package's Feature Configs */ + std::map<std::string, std::vector<FeatureConfig>> mPackageFeatures; +}; + +} // namespace android diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 452e48bb75..55fa13abb5 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -17,6 +17,7 @@ #ifndef ANDROID_UI_GRAPHICS_ENV_H #define ANDROID_UI_GRAPHICS_ENV_H 1 +#include <graphicsenv/FeatureOverrides.h> #include <graphicsenv/GpuStatsInfo.h> #include <mutex> @@ -120,6 +121,8 @@ public: // Get the app package name. std::string& getPackageName(); const std::vector<std::string>& getAngleEglFeatures(); + void getAngleFeatureOverrides(std::vector<const char*>& enabled, + std::vector<const char*>& disabled); // Set the persist.graphics.egl system property value. void nativeToggleAngleAsSystemDriver(bool enabled); bool shouldUseSystemAngle(); @@ -177,6 +180,7 @@ private: std::string mPackageName; // ANGLE EGL features; std::vector<std::string> mAngleEglFeatures; + FeatureOverrides mFeatureOverrides; // Whether ANGLE should be used. bool mShouldUseAngle = false; // Whether loader should load system ANGLE. diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 38465b0de2..0848fac293 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -244,12 +244,6 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati BQA_LOGV("BLASTBufferQueue created"); } -BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, - int width, int height, int32_t format) - : BLASTBufferQueue(name) { - update(surface, width, height, format); -} - BLASTBufferQueue::~BLASTBufferQueue() { TransactionCompletedListener::getInstance()->removeQueueStallListener(this); if (mPendingTransactions.empty()) { @@ -1227,16 +1221,12 @@ public: #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) status_t waitForBufferRelease(std::unique_lock<std::mutex>& bufferQueueLock, nsecs_t timeout) const override { + const auto startTime = std::chrono::steady_clock::now(); sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); if (!bbq) { return OK; } - // Provide a callback for Choreographer to start buffer stuffing recovery when blocked - // on buffer release. - std::function<void()> callbackCopy = bbq->getWaitForBufferReleaseCallback(); - if (callbackCopy) callbackCopy(); - // BufferQueue has already checked if we have a free buffer. If there's an unread interrupt, // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. @@ -1258,6 +1248,14 @@ public: } bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount); + const nsecs_t durationNanos = std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::steady_clock::now() - startTime) + .count(); + // Provide a callback for Choreographer to start buffer stuffing recovery when blocked + // on buffer release. + std::function<void(const nsecs_t)> callbackCopy = bbq->getWaitForBufferReleaseCallback(); + if (callbackCopy) callbackCopy(durationNanos); + return OK; } #endif @@ -1349,12 +1347,13 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { mApplyToken = std::move(applyToken); } -void BLASTBufferQueue::setWaitForBufferReleaseCallback(std::function<void()> callback) { +void BLASTBufferQueue::setWaitForBufferReleaseCallback( + std::function<void(const nsecs_t)> callback) { std::lock_guard _lock{mWaitForBufferReleaseMutex}; mWaitForBufferReleaseCallback = std::move(callback); } -std::function<void()> BLASTBufferQueue::getWaitForBufferReleaseCallback() const { +std::function<void(const nsecs_t)> BLASTBufferQueue::getWaitForBufferReleaseCallback() const { std::lock_guard _lock{mWaitForBufferReleaseMutex}; return mWaitForBufferReleaseCallback; } diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index f0125868ae..270bfbdc64 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -477,9 +477,14 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) +status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, + const sp<Fence>& releaseFence) { +#else status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, const sp<Fence>& releaseFence, EGLDisplay eglDisplay, EGLSyncKHR eglFence) { +#endif ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); @@ -493,27 +498,6 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, return BAD_VALUE; } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) - if (eglFence != EGL_NO_SYNC_KHR) { - // Most platforms will be using native fences, so it's unlikely that we'll ever have to - // process an eglFence. Ideally we can remove this code eventually. In the mean time, do our - // best to wait for it so the buffer stays valid, otherwise return an error to the caller. - // - // EGL_SYNC_FLUSH_COMMANDS_BIT_KHR so that we don't wait forever on a fence that hasn't - // shown up on the GPU yet. - EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, - 1000000000); - if (result == EGL_FALSE) { - BQ_LOGE("releaseBuffer: error %#x waiting for fence", eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - BQ_LOGE("releaseBuffer: timeout waiting for fence"); - return UNKNOWN_ERROR; - } - eglDestroySyncKHR(eglDisplay, eglFence); - } -#endif - sp<IProducerListener> listener; { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 504509dbec..3ad0e529a5 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -656,9 +656,13 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, return OK; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) +status_t ConsumerBase::releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer) { +#else status_t ConsumerBase::releaseBufferLocked( int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) { +#endif if (mAbandoned) { CB_LOGE("releaseBufferLocked: ConsumerBase is abandoned!"); return NO_INIT; @@ -675,8 +679,12 @@ status_t ConsumerBase::releaseBufferLocked( CB_LOGV("releaseBufferLocked: slot=%d/%" PRIu64, slot, mSlots[slot].mFrameNumber); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence); +#else status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, display, eglFence, mSlots[slot].mFence); +#endif if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 168129b1f7..052b8edfaa 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -417,18 +417,18 @@ void GLConsumer::onSlotCountChanged(int slotCount) { } #endif -status_t GLConsumer::releaseBufferLocked(int buf, - sp<GraphicBuffer> graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) { +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) +status_t GLConsumer::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { // release the buffer if it hasn't already been discarded by the // BufferQueue. This can happen, for example, when the producer of this // buffer has reallocated the original buffer slot after this buffer // was acquired. - status_t err = ConsumerBase::releaseBufferLocked( - buf, graphicBuffer, display, eglFence); + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; return err; } +#endif status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease) @@ -490,9 +490,14 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (pendingRelease == nullptr) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t status = + releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer()); +#else status_t status = releaseBufferLocked( mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); +#endif if (status < NO_ERROR) { GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); @@ -501,10 +506,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, } } else { pendingRelease->currentTexture = mCurrentTexture; - pendingRelease->graphicBuffer = - mCurrentTextureImage->graphicBuffer(); - pendingRelease->display = mEglDisplay; - pendingRelease->fence = mEglSlots[mCurrentTexture].mEglFence; + pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); pendingRelease->isPending = true; } } @@ -744,6 +746,11 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { return err; } } else if (mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + // Basically all clients are using native fence syncs. If they aren't, we lose nothing + // by waiting here, because the alternative can cause deadlocks (b/339705065). + glFinish(); +#else EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to @@ -773,6 +780,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { } glFlush(); mEglSlots[mCurrentTexture].mEglFence = fence; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) } } diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp index c1b65689d6..e133532e02 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -85,6 +85,12 @@ public: return callRemote<Signature>(Tag::ATTACH_BUFFER, slot, buffer); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) override { + using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&); + return callRemote<Signature>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); + } +#else status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)), @@ -92,6 +98,7 @@ public: using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&); return callRemote<Signature>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); } +#endif status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override { using Signature = decltype(&IGraphicBufferConsumer::consumerConnect); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 2beeae0201..852885be61 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -19,6 +19,7 @@ #include <semaphore.h> #include <stdint.h> #include <sys/types.h> +#include <algorithm> #include <android/gui/BnWindowInfosReportedListener.h> #include <android/gui/DisplayState.h> @@ -829,9 +830,7 @@ SurfaceComposerClient::Transaction::Transaction() { SurfaceComposerClient::Transaction::Transaction(const Transaction& other) : mId(other.mId), - mAnimation(other.mAnimation), - mEarlyWakeupStart(other.mEarlyWakeupStart), - mEarlyWakeupEnd(other.mEarlyWakeupEnd), + mFlags(other.mFlags), mMayContainBuffer(other.mMayContainBuffer), mDesiredPresentTime(other.mDesiredPresentTime), mIsAutoTimestamp(other.mIsAutoTimestamp), @@ -846,7 +845,7 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) { uint32_t permissions = LayerStatePermissions::getTransactionPermissions(pid, uid); - for (auto & [handle, composerState] : mComposerStates) { + for (auto& composerState : mComposerStates) { composerState.state.sanitize(permissions); } if (!mInputWindowCommands.empty() && @@ -868,9 +867,7 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) { const uint64_t transactionId = parcel->readUint64(); - const bool animation = parcel->readBool(); - const bool earlyWakeupStart = parcel->readBool(); - const bool earlyWakeupEnd = parcel->readBool(); + const uint32_t flags = parcel->readUint32(); const int64_t desiredPresentTime = parcel->readInt64(); const bool isAutoTimestamp = parcel->readBool(); const bool logCallPoints = parcel->readBool(); @@ -883,7 +880,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel if (count > parcel->dataSize()) { return BAD_VALUE; } - SortedVector<DisplayState> displayStates; + Vector<DisplayState> displayStates; displayStates.setCapacity(count); for (size_t i = 0; i < count; i++) { DisplayState displayState; @@ -926,17 +923,14 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel if (count > parcel->dataSize()) { return BAD_VALUE; } - std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates; - composerStates.reserve(count); + Vector<ComposerState> composerStates; + composerStates.setCapacity(count); for (size_t i = 0; i < count; i++) { - sp<IBinder> surfaceControlHandle; - SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle); - ComposerState composerState; if (composerState.read(*parcel) == BAD_VALUE) { return BAD_VALUE; } - composerStates[surfaceControlHandle] = composerState; + composerStates.add(composerState); } InputWindowCommands inputWindowCommands; @@ -965,15 +959,13 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel // Parsing was successful. Update the object. mId = transactionId; - mAnimation = animation; - mEarlyWakeupStart = earlyWakeupStart; - mEarlyWakeupEnd = earlyWakeupEnd; + mFlags = flags; mDesiredPresentTime = desiredPresentTime; mIsAutoTimestamp = isAutoTimestamp; mFrameTimelineInfo = frameTimelineInfo; - mDisplayStates = displayStates; + mDisplayStates = std::move(displayStates); mListenerCallbacks = listenerCallbacks; - mComposerStates = composerStates; + mComposerStates = std::move(composerStates); mInputWindowCommands = inputWindowCommands; mApplyToken = applyToken; mUncacheBuffers = std::move(uncacheBuffers); @@ -996,9 +988,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers(); parcel->writeUint64(mId); - parcel->writeBool(mAnimation); - parcel->writeBool(mEarlyWakeupStart); - parcel->writeBool(mEarlyWakeupEnd); + parcel->writeUint32(mFlags); parcel->writeInt64(mDesiredPresentTime); parcel->writeBool(mIsAutoTimestamp); parcel->writeBool(mLogCallPoints); @@ -1023,8 +1013,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const } parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size())); - for (auto const& [handle, composerState] : mComposerStates) { - SAFE_PARCEL(parcel->writeStrongBinder, handle); + for (auto const& composerState : mComposerStates) { composerState.write(*parcel); } @@ -1081,23 +1070,31 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr } mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId); - for (auto const& [handle, composerState] : other.mComposerStates) { - if (mComposerStates.count(handle) == 0) { - mComposerStates[handle] = composerState; - } else { - if (composerState.state.what & layer_state_t::eBufferChanged) { - releaseBufferIfOverwriting(mComposerStates[handle].state); + for (auto const& otherState : other.mComposerStates) { + if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(), + [&otherState](const auto& composerState) { + return composerState.state.surface == + otherState.state.surface; + }); + it != mComposerStates.end()) { + if (otherState.state.what & layer_state_t::eBufferChanged) { + releaseBufferIfOverwriting(it->state); } - mComposerStates[handle].state.merge(composerState.state); + it->state.merge(otherState.state); + } else { + mComposerStates.add(otherState); } } for (auto const& state : other.mDisplayStates) { - ssize_t index = mDisplayStates.indexOf(state); - if (index < 0) { - mDisplayStates.add(state); + if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(), + [&state](const auto& displayState) { + return displayState.token == state.token; + }); + it != mDisplayStates.end()) { + it->merge(state); } else { - mDisplayStates.editItemAt(static_cast<size_t>(index)).merge(state); + mDisplayStates.add(state); } } @@ -1131,8 +1128,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mInputWindowCommands.merge(other.mInputWindowCommands); mMayContainBuffer |= other.mMayContainBuffer; - mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart; - mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd; + mFlags |= other.mFlags; mApplyToken = other.mApplyToken; mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo); @@ -1154,15 +1150,13 @@ void SurfaceComposerClient::Transaction::clear() { mInputWindowCommands.clear(); mUncacheBuffers.clear(); mMayContainBuffer = false; - mAnimation = false; - mEarlyWakeupStart = false; - mEarlyWakeupEnd = false; mDesiredPresentTime = 0; mIsAutoTimestamp = true; mFrameTimelineInfo = {}; mApplyToken = nullptr; mMergedTransactionIds.clear(); mLogCallPoints = false; + mFlags = 0; } uint64_t SurfaceComposerClient::Transaction::getId() { @@ -1197,8 +1191,8 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { } size_t count = 0; - for (auto& [handle, cs] : mComposerStates) { - layer_state_t* s = &(mComposerStates[handle].state); + for (auto& cs : mComposerStates) { + layer_state_t* s = &cs.state; if (!(s->what & layer_state_t::eBufferChanged)) { continue; } else if (s->bufferData && @@ -1323,42 +1317,26 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay cacheBuffers(); - Vector<ComposerState> composerStates; - Vector<DisplayState> displayStates; - uint32_t flags = 0; - - for (auto const& kv : mComposerStates) { - composerStates.add(kv.second); - } - - displayStates = std::move(mDisplayStates); - - if (mAnimation) { - flags |= ISurfaceComposer::eAnimation; - } if (oneWay) { if (synchronous) { ALOGE("Transaction attempted to set synchronous and one way at the same time" " this is an invalid request. Synchronous will win for safety"); } else { - flags |= ISurfaceComposer::eOneWay; + mFlags |= ISurfaceComposer::eOneWay; } } - // If both mEarlyWakeupStart and mEarlyWakeupEnd are set + // If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set // it is equivalent for none - if (mEarlyWakeupStart && !mEarlyWakeupEnd) { - flags |= ISurfaceComposer::eEarlyWakeupStart; - } - if (mEarlyWakeupEnd && !mEarlyWakeupStart) { - flags |= ISurfaceComposer::eEarlyWakeupEnd; + uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd; + if ((mFlags & wakeupFlags) == wakeupFlags) { + mFlags &= ~(wakeupFlags); } - sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken(); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); status_t binderStatus = - sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, + sf->setTransactionState(mFrameTimelineInfo, mComposerStates, mDisplayStates, mFlags, applyToken, mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId, mMergedTransactionIds); @@ -1461,31 +1439,34 @@ std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTran } void SurfaceComposerClient::Transaction::setAnimationTransaction() { - mAnimation = true; + mFlags |= ISurfaceComposer::eAnimation; } void SurfaceComposerClient::Transaction::setEarlyWakeupStart() { - mEarlyWakeupStart = true; + mFlags |= ISurfaceComposer::eEarlyWakeupStart; } void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() { - mEarlyWakeupEnd = true; + mFlags |= ISurfaceComposer::eEarlyWakeupEnd; } layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) { auto handle = sc->getLayerStateHandle(); - - if (mComposerStates.count(handle) == 0) { - // we don't have it, add an initialized layer_state to our list - ComposerState s; - - s.state.surface = handle; - s.state.layerId = sc->getLayerId(); - - mComposerStates[handle] = s; + if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(), + [&handle](const auto& composerState) { + return composerState.state.surface == handle; + }); + it != mComposerStates.end()) { + return &it->state; } - return &(mComposerStates[handle].state); + // we don't have it, add an initialized layer_state to our list + ComposerState s; + s.state.surface = handle; + s.state.layerId = sc->getLayerId(); + mComposerStates.add(s); + + return &mComposerStates.editItemAt(mComposerStates.size() - 1).state; } void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( @@ -2510,15 +2491,17 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setConte // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { + if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(), + [token](const auto& display) { return display.token == token; }); + it != mDisplayStates.end()) { + return *it; + } + + // If display state doesn't exist, add a new one. DisplayState s; s.token = token; - ssize_t index = mDisplayStates.indexOf(s); - if (index < 0) { - // we don't have it, add an initialized layer_state to our list - s.what = 0; - index = mDisplayStates.add(s); - } - return mDisplayStates.editItemAt(static_cast<size_t>(index)); + mDisplayStates.add(s); + return mDisplayStates.editItemAt(mDisplayStates.size() - 1); } status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>& token, diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index f126c0be2f..b735418d4b 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -141,7 +141,8 @@ sp<Surface> SurfaceControl::generateSurfaceLocked() ISurfaceComposerClient::eOpaque); mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat, flags, mHandle, {}, &ignore); - mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat); + mBbq = sp<BLASTBufferQueue>::make("[BBQ] " + mName, /* updateDestinationFrame */ true); + mBbq->update(mBbqChild, mWidth, mHeight, mFormat); // This surface is always consumed by SurfaceFlinger, so the // producerControlledByApp value doesn't matter; using false. diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 1bc1dd0685..b97a49616d 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -90,8 +90,6 @@ private: class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener { public: BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true); - BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, - int height, int32_t format); sp<IGraphicBufferProducer> getIGraphicBufferProducer() const { return mProducer; @@ -145,9 +143,9 @@ public: void setTransactionHangCallback(std::function<void(const std::string&)> callback); void setApplyToken(sp<IBinder>); - void setWaitForBufferReleaseCallback(std::function<void()> callback) + void setWaitForBufferReleaseCallback(std::function<void(const nsecs_t)> callback) EXCLUDES(mWaitForBufferReleaseMutex); - std::function<void()> getWaitForBufferReleaseCallback() const + std::function<void(const nsecs_t)> getWaitForBufferReleaseCallback() const EXCLUDES(mWaitForBufferReleaseMutex); virtual ~BLASTBufferQueue(); @@ -331,7 +329,8 @@ private: std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); - std::function<void()> mWaitForBufferReleaseCallback GUARDED_BY(mWaitForBufferReleaseMutex); + std::function<void(const nsecs_t)> mWaitForBufferReleaseCallback + GUARDED_BY(mWaitForBufferReleaseMutex); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to the // client. diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h index e00c44eb70..f99b54beb1 100644 --- a/libs/gui/include/gui/BufferQueueConsumer.h +++ b/libs/gui/include/gui/BufferQueueConsumer.h @@ -65,13 +65,14 @@ public: // any references to the just-released buffer that it might have, as if it // had received a onBuffersReleased() call with a mask set for the released // buffer. - // - // Note that the dependencies on EGL will be removed once we switch to using - // the Android HW Sync HAL. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + virtual status_t releaseBuffer(int slot, uint64_t frameNumber, + const sp<Fence>& releaseFence) override; +#else virtual status_t releaseBuffer(int slot, uint64_t frameNumber, const sp<Fence>& releaseFence, EGLDisplay display, EGLSyncKHR fence); - +#endif // connect connects a consumer to the BufferQueue. Only one // consumer may be connected, and when that consumer disconnects the // BufferQueue is placed into the "abandoned" state, causing most @@ -167,6 +168,7 @@ public: // dump our state in a String status_t dumpState(const String8& prefix, String8* outResult) const override; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) // Functions required for backwards compatibility. // These will be modified/renamed in IGraphicBufferConsumer and will be // removed from this class at that time. See b/13306289. @@ -176,6 +178,7 @@ public: const sp<Fence>& releaseFence) { return releaseBuffer(buf, frameNumber, releaseFence, display, fence); } +#endif virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) { diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h index 5cd19c1583..acb0006754 100644 --- a/libs/gui/include/gui/ConsumerBase.h +++ b/libs/gui/include/gui/ConsumerBase.h @@ -245,10 +245,13 @@ protected: // must take place when a buffer is released back to the BufferQueue. If // it is overridden the derived class's implementation must call // ConsumerBase::releaseBufferLocked. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer); +#else virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR); - +#endif // returns true iff the slot still has the graphicBuffer in it. bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer); diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 30cbfa2f9f..dbf707f35f 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -271,6 +271,7 @@ protected: #endif // releaseBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase. +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override; @@ -279,16 +280,14 @@ protected: const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) { return releaseBufferLocked(slot, graphicBuffer, mEglDisplay, eglFence); } +#endif struct PendingRelease { - PendingRelease() : isPending(false), currentTexture(-1), - graphicBuffer(), display(nullptr), fence(nullptr) {} + PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {} bool isPending; int currentTexture; sp<GraphicBuffer> graphicBuffer; - EGLDisplay display; - EGLSyncKHR fence; }; // This releases the buffer in the slot referenced by mCurrentTexture, @@ -468,16 +467,18 @@ private: // EGLSlot contains the information and object references that // GLConsumer maintains about a BufferQueue buffer slot. struct EglSlot { +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} - +#endif // mEglImage is the EGLImage created from mGraphicBuffer. sp<EglImage> mEglImage; - +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) // mFence is the EGL sync object that must signal before the buffer // associated with this buffer slot may be dequeued. It is initialized // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based // on a compile-time option) set to a new sync object in updateTexImage. EGLSyncKHR mEglFence; +#endif }; // mEglDisplay is the EGLDisplay with which this GLConsumer is currently diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h index 56eb291335..f6b3e894c2 100644 --- a/libs/gui/include/gui/IGraphicBufferConsumer.h +++ b/libs/gui/include/gui/IGraphicBufferConsumer.h @@ -139,12 +139,17 @@ public: // * the buffer slot was invalid // * the fence was NULL // * the buffer slot specified is not in the acquired state +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, + const sp<Fence>& releaseFence) = 0; +#else virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0; status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) { return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); } +#endif // consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected, // and when that consumer disconnects the BufferQueue is placed into the "abandoned" state, diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index d20b3460b5..d30a830863 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -455,8 +455,8 @@ public: bool mLogCallPoints = false; protected: - std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates; - SortedVector<DisplayState> mDisplayStates; + Vector<ComposerState> mComposerStates; + Vector<DisplayState> mDisplayStates; std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> mListenerCallbacks; std::vector<client_cache_t> mUncacheBuffers; @@ -467,10 +467,7 @@ public: std::vector<uint64_t> mMergedTransactionIds; uint64_t mId; - - bool mAnimation = false; - bool mEarlyWakeupStart = false; - bool mEarlyWakeupEnd = false; + uint32_t mFlags = 0; // Indicates that the Transaction may contain buffers that should be cached. The reason this // is only a guess is that buffers can be removed before cache is called. This is only a diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h index 98f24c2d44..8dfd3cb1f7 100644 --- a/libs/gui/include/gui/mock/GraphicBufferConsumer.h +++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h @@ -18,6 +18,7 @@ #include <gmock/gmock.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/IGraphicBufferConsumer.h> #include <utils/RefBase.h> @@ -33,7 +34,11 @@ public: MOCK_METHOD3(acquireBuffer, status_t(BufferItem*, nsecs_t, uint64_t)); MOCK_METHOD1(detachBuffer, status_t(int)); MOCK_METHOD2(attachBuffer, status_t(int*, const sp<GraphicBuffer>&)); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + MOCK_METHOD3(releaseBuffer, status_t(int, uint64_t, const sp<Fence>&)); +#else MOCK_METHOD5(releaseBuffer, status_t(int, uint64_t, EGLDisplay, EGLSyncKHR, const sp<Fence>&)); +#endif MOCK_METHOD2(consumerConnect, status_t(const sp<IConsumerListener>&, bool)); MOCK_METHOD0(consumerDisconnect, status_t()); MOCK_METHOD1(getReleasedBuffers, status_t(uint64_t*)); diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 394a5cf5c5..90d91ac06a 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -142,3 +142,11 @@ flag { purpose: PURPOSE_BUGFIX } } # bq_gl_fence_cleanup + +flag { + name: "wb_media_migration" + namespace: "core_graphics" + description: "Main flag for the warren buffers media migration." + bug: "340934031" + is_fixed_read_only: true +} # wb_media_migration diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 53f4a36c42..e6ee89f9b2 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -81,7 +81,9 @@ class TestBLASTBufferQueue : public BLASTBufferQueue { public: TestBLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, int height, int32_t format) - : BLASTBufferQueue(name, surface, width, height, format) {} + : BLASTBufferQueue(name) { + update(surface, width, height, format); + } void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats) override { diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 06f00a4d74..8062a2e6a2 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -85,6 +85,7 @@ sp<IInputFlinger> getInputFlinger() { // We use the top 10 layers as a way to haphazardly place ourselves above anything else. static const int LAYER_BASE = INT32_MAX - 10; static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; +static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener { public: @@ -203,8 +204,8 @@ public: ASSERT_EQ(InputEventType::MOTION, ev->getType()); MotionEvent* mev = static_cast<MotionEvent*>(ev); EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); - EXPECT_EQ(x, mev->getX(0)); - EXPECT_EQ(y, mev->getY(0)); + EXPECT_NEAR(x, mev->getX(0), EPSILON); + EXPECT_NEAR(y, mev->getY(0), EPSILON); EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS); ev = consumeEvent(); diff --git a/libs/input/AccelerationCurve.cpp b/libs/input/AccelerationCurve.cpp index 0b47f3e6e7..1ed9794105 100644 --- a/libs/input/AccelerationCurve.cpp +++ b/libs/input/AccelerationCurve.cpp @@ -47,9 +47,9 @@ constexpr std::array<double, 15> kSensitivityFactors = {1, 2, 4, 6, 7, 8, // in faster pointer movements. // // The base gain is calculated using a linear mapping function that maps the -// sensitivity range [-7, 7] to a base gain range [0.5, 2.0]. +// sensitivity range [-7, 7] to a base gain range [1.0, 3.5]. double calculateBaseGain(int32_t sensitivity) { - return 0.5 + (sensitivity + 7) * (2.0 - 0.5) / (7 + 7); + return 1.0 + (sensitivity + 7) * (3.5 - 1.0) / (7 + 7); } } // namespace diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index bf928f4847..72a6fdf1bb 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -79,13 +79,6 @@ flag { } flag { - name: "enable_input_filter_rust_impl" - namespace: "input" - description: "Enable input filter rust implementation" - bug: "294546335" -} - -flag { name: "override_key_behavior_permission_apis" is_exported: true namespace: "input" @@ -244,3 +237,11 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "deprecate_uiautomation_input_injection" + namespace: "input" + description: "Deprecate UiAutomation#injectInputEvent and UiAutomation#injectInputEventToInputFilter test/hidden APIs" + bug: "277261245" +} + diff --git a/libs/input/rust/keyboard_classification_config.rs b/libs/input/rust/keyboard_classification_config.rs index ab74efb674..26a8d8f4b8 100644 --- a/libs/input/rust/keyboard_classification_config.rs +++ b/libs/input/rust/keyboard_classification_config.rs @@ -20,6 +20,15 @@ use crate::input::KeyboardType; // key events at all. (Requires setup allowing InputDevice to dynamically add/remove // KeyboardInputMapper based on blocklist and KeyEvents in case a KeyboardType::None device ends // up producing a key event) + +/// This list pre-classifies a device into Alphabetic/Non-Alphabetic keyboard and tells us whether +/// further re-classification should be allowed or not (using is_finalized value). +/// This list DOES NOT change the source of the device or change the input mappers associated with +/// the device. It only changes the "KeyboardType" classification. This list should be primarily +/// used to pre-classify devices that are NOT keyboards(like mice, game pads, etc.) but generate +/// evdev nodes that say that they are alphabetic keyboards. +/// +/// NOTE: Pls keep the list sorted by vendor id and product id for easy searching. pub static CLASSIFIED_DEVICES: &[( /* vendorId */ u16, /* productId */ u16, @@ -96,6 +105,8 @@ pub static CLASSIFIED_DEVICES: &[( (0x056e, 0x0159, KeyboardType::NonAlphabetic, true), // Zebra LS2208 barcode scanner (0x05e0, 0x1200, KeyboardType::NonAlphabetic, true), + // Glorious O2 Wireless + (0x093a, 0x822d, KeyboardType::NonAlphabetic, true), // RDing FootSwitch1F1 (0x0c45, 0x7403, KeyboardType::NonAlphabetic, true), // SteelSeries Sensei RAW Frost Blue @@ -108,6 +119,8 @@ pub static CLASSIFIED_DEVICES: &[( (0x1050, 0x0010, KeyboardType::NonAlphabetic, true), // Yubico.com Yubikey 4 OTP+U2F+CCID (0x1050, 0x0407, KeyboardType::NonAlphabetic, true), + // Razer DeathAdder Essential + (0x1532, 0x0098, KeyboardType::NonAlphabetic, true), // Lenovo USB-C Wired Compact Mouse (0x17ef, 0x6123, KeyboardType::NonAlphabetic, true), // Corsair Katar Pro Wireless (USB dongle) diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs index 3c789b41e2..1b89a5cf2b 100644 --- a/libs/input/rust/keyboard_classifier.rs +++ b/libs/input/rust/keyboard_classifier.rs @@ -66,11 +66,11 @@ impl KeyboardClassifier { /// Get keyboard type for a tracked keyboard in KeyboardClassifier pub fn get_keyboard_type(&self, device_id: DeviceId) -> KeyboardType { - return if let Some(keyboard) = self.device_map.get(&device_id) { + if let Some(keyboard) = self.device_map.get(&device_id) { keyboard.keyboard_type } else { KeyboardType::None - }; + } } /// Tells if keyboard type classification is finalized. Once finalized the classification can't @@ -79,11 +79,11 @@ impl KeyboardClassifier { /// Finalized devices are either "alphabetic" keyboards or keyboards in blocklist or /// allowlist that are explicitly categorized and won't change with future key events pub fn is_finalized(&self, device_id: DeviceId) -> bool { - return if let Some(keyboard) = self.device_map.get(&device_id) { + if let Some(keyboard) = self.device_map.get(&device_id) { keyboard.is_finalized } else { false - }; + } } /// Process a key event and change keyboard type if required. diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp index e2eb08096b..5bb1d56040 100644 --- a/libs/input/tests/InputVerifier_test.cpp +++ b/libs/input/tests/InputVerifier_test.cpp @@ -14,9 +14,13 @@ * limitations under the License. */ +#include <android/input.h> +#include <android-base/result.h> #include <gtest/gtest.h> +#include <input/Input.h> #include <input/InputVerifier.h> #include <string> +#include <vector> namespace android { @@ -48,7 +52,7 @@ TEST(InputVerifierTest, ProcessSourceClassPointer) { AMOTION_EVENT_ACTION_DOWN, /*pointerCount=*/properties.size(), properties.data(), coords.data(), /*flags=*/0); - ASSERT_TRUE(result.ok()); + ASSERT_RESULT_OK(result); } } // namespace android diff --git a/libs/nativedisplay/include/surfacetexture/EGLConsumer.h b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h index 444722bf83..226a8a60af 100644 --- a/libs/nativedisplay/include/surfacetexture/EGLConsumer.h +++ b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h @@ -113,18 +113,11 @@ public: protected: struct PendingRelease { - PendingRelease() - : isPending(false), - currentTexture(-1), - graphicBuffer(), - display(nullptr), - fence(nullptr) {} + PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {} bool isPending; int currentTexture; sp<GraphicBuffer> graphicBuffer; - EGLDisplay display; - EGLSyncKHR fence; }; /** @@ -250,13 +243,16 @@ protected: * EGLConsumer maintains about a BufferQueue buffer slot. */ struct EglSlot { - EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} +#endif /** * mEglImage is the EGLImage created from mGraphicBuffer. */ sp<EglImage> mEglImage; +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) /** * mFence is the EGL sync object that must signal before the buffer * associated with this buffer slot may be dequeued. It is initialized @@ -264,6 +260,7 @@ protected: * on a compile-time option) set to a new sync object in updateTexImage. */ EGLSyncKHR mEglFence; +#endif }; /** diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index 006a785cb7..253aa18a24 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -343,9 +343,13 @@ protected: * releaseBufferLocked overrides the ConsumerBase method to update the * mEglSlots array in addition to the ConsumerBase. */ +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer) override; +#else virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override; +#endif /** * freeBufferLocked frees up the given buffer slot. If the slot has been diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 3959fce008..fad0f6cd0b 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -221,7 +221,11 @@ void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { } void EGLConsumer::onReleaseBufferLocked(int buf) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + (void)buf; +#else mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +#endif } status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, @@ -283,10 +287,15 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele // release old buffer if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (pendingRelease == nullptr) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t status = st.releaseBufferLocked(st.mCurrentTexture, + mCurrentTextureImage->graphicBuffer()); +#else status_t status = st.releaseBufferLocked(st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, mEglSlots[st.mCurrentTexture].mEglFence); +#endif if (status < NO_ERROR) { EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); @@ -296,8 +305,6 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele } else { pendingRelease->currentTexture = st.mCurrentTexture; pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); - pendingRelease->display = mEglDisplay; - pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; pendingRelease->isPending = true; } } @@ -502,6 +509,11 @@ status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { return err; } } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + // Basically all clients are using native fence syncs. If they aren't, we lose nothing + // by waiting here, because the alternative can cause deadlocks (b/339705065). + glFinish(); +#else EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to @@ -531,6 +543,7 @@ status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { } glFlush(); mEglSlots[st.mCurrentTexture].mEglFence = fence; +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) } } diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index 60e87b54d5..1ffd382b80 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#define EGL_EGLEXT_PROTOTYPES +#include <EGL/egl.h> +#include <EGL/eglext.h> + #include <gui/BufferQueue.h> #include <surfacetexture/ImageConsumer.h> #include <surfacetexture/SurfaceTexture.h> @@ -95,10 +99,34 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace } // Finally release the old buffer. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + EGLSyncKHR previousFence = mImageSlots[st.mCurrentTexture].eglFence(); + if (previousFence != EGL_NO_SYNC_KHR) { + // Most platforms will be using native fences, so it's unlikely that we'll ever have to + // process an eglFence. Ideally we can remove this code eventually. In the mean time, do + // our best to wait for it so the buffer stays valid, otherwise return an error to the + // caller. + // + // EGL_SYNC_FLUSH_COMMANDS_BIT_KHR so that we don't wait forever on a fence that hasn't + // shown up on the GPU yet. + EGLint result = eglClientWaitSyncKHR(display, previousFence, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 1000000000); + if (result == EGL_FALSE) { + IMG_LOGE("dequeueBuffer: error %#x waiting for fence", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + IMG_LOGE("dequeueBuffer: timeout waiting for fence"); + } + eglDestroySyncKHR(display, previousFence); + } + + status_t status = st.releaseBufferLocked(st.mCurrentTexture, + st.mSlots[st.mCurrentTexture].mGraphicBuffer); +#else status_t status = st.releaseBufferLocked(st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, mImageSlots[st.mCurrentTexture].eglFence()); +#endif if (status < NO_ERROR) { IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); err = status; diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index ce232cc4c7..c0a1cc5c36 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -178,13 +178,21 @@ status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWh return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) +status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer) { +#else status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) { +#endif // release the buffer if it hasn't already been discarded by the // BufferQueue. This can happen, for example, when the producer of this // buffer has reallocated the original buffer slot after this buffer // was acquired. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP) + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer); +#else status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); +#endif // We could be releasing an EGL/Vulkan buffer, even if not currently // attached to a GL context. mImageConsumer.onReleaseBufferLocked(buf); diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index d760285918..aeb26031a5 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -541,7 +541,7 @@ pub struct HardwareBufferGuard<'a> { pub address: NonNull<c_void>, } -impl<'a> Drop for HardwareBufferGuard<'a> { +impl Drop for HardwareBufferGuard<'_> { fn drop(&mut self) { self.buffer .unlock() diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 14d08eea74..5f2d1b1be6 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -838,8 +838,7 @@ void SkiaRenderEngine::drawLayersInternal( LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface); LOG_ALWAYS_FATAL_IF(canvas == dstCanvas); - // save a snapshot of the activeSurface to use as input to the blur shaders - blurInput = activeSurface->makeImageSnapshot(); + blurInput = activeSurface->makeTemporaryImage(); // blit the offscreen framebuffer into the destination AHB. This ensures that // even if the blurred image does not cover the screen (for example, during @@ -853,12 +852,9 @@ void SkiaRenderEngine::drawLayersInternal( dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()), String8::format("SurfaceID|%" PRId64, id).c_str(), nullptr); - dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); - } else { - activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint); } + dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); } - // assign dstCanvas to canvas and ensure that the canvas state is up to date canvas = dstCanvas; surfaceAutoSaveRestore.replace(canvas); @@ -891,12 +887,6 @@ void SkiaRenderEngine::drawLayersInternal( if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) { std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; - // if multiple layers have blur, then we need to take a snapshot now because - // only the lowest layer will have blurImage populated earlier - if (!blurInput) { - blurInput = activeSurface->makeImageSnapshot(); - } - // rect to be blurred in the coordinate space of blurInput SkRect blurRect = canvas->getTotalMatrix().mapRect(bounds.rect()); @@ -920,6 +910,29 @@ void SkiaRenderEngine::drawLayersInternal( // TODO(b/182216890): Filter out empty layers earlier if (blurRect.width() > 0 && blurRect.height() > 0) { + // if multiple layers have blur, then we need to take a snapshot now because + // only the lowest layer will have blurImage populated earlier + if (!blurInput) { + bool requiresCrossFadeWithBlurInput = false; + if (layer.backgroundBlurRadius > 0 && + layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) { + requiresCrossFadeWithBlurInput = true; + } + for (auto region : layer.blurRegions) { + if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) { + requiresCrossFadeWithBlurInput = true; + } + } + if (requiresCrossFadeWithBlurInput) { + // If we require cross fading with the blur input, we need to make sure we + // make a copy of the surface to the image since we will be writing to the + // surface while sampling the blurInput. + blurInput = activeSurface->makeImageSnapshot(); + } else { + blurInput = activeSurface->makeTemporaryImage(); + } + } + if (layer.backgroundBlurRadius > 0) { SFTRACE_NAME("BackgroundBlur"); auto blurredImage = mBlurFilter->generate(context, layer.backgroundBlurRadius, diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index cd1bd71807..8edf98eb89 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -90,6 +90,8 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, linearSampling, &blurMatrix); if (blurRadius < mMaxCrossFadeRadius) { + LOG_ALWAYS_FATAL_IF(!input); + // For sampling Skia's API expects the inverse of what logically seems appropriate. In this // case you might expect the matrix to simply be the canvas matrix. SkMatrix inputMatrix; diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp index 8c52c571a9..b03ebe3353 100644 --- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp +++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp @@ -64,7 +64,7 @@ sk_sp<SkImage> GaussianBlurFilter::generate(SkiaGpuContext* context, const uint3 SkSamplingOptions{SkFilterMode::kLinear, SkMipmapMode::kNone}, &paint, SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint); - return surface->makeImageSnapshot(); + return surface->makeTemporaryImage(); } } // namespace skia diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp index ef57c30563..897f5cfc2a 100644 --- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp @@ -167,14 +167,14 @@ sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin } // Next the remaining downscale blur passes. for (int i = 0; i < filterPasses; i++) { - blurInto(surfaces[i + 1], surfaces[i]->makeImageSnapshot(), kWeights[1 + i] * step, 1.0f); + blurInto(surfaces[i + 1], surfaces[i]->makeTemporaryImage(), kWeights[1 + i] * step, 1.0f); } // Finally blur+upscale back to our original size. for (int i = filterPasses - 1; i >= 0; i--) { - blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[4 - i] * step, + blurInto(surfaces[i], surfaces[i + 1]->makeTemporaryImage(), kWeights[4 - i] * step, std::min(1.0f, filterDepth - i)); } - return surfaces[0]->makeImageSnapshot(); + return surfaces[0]->makeTemporaryImage(); } } // namespace skia diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index defaf6e476..f71a63d591 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -70,7 +70,7 @@ static sk_sp<SkImage> makeImage(SkSurface* surface, SkRuntimeShaderBuilder* buil paint.setShader(std::move(shader)); paint.setBlendMode(SkBlendMode::kSrc); surface->getCanvas()->drawPaint(paint); - return surface->makeImageSnapshot(); + return surface->makeTemporaryImage(); } sk_sp<SkImage> KawaseBlurFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius, diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp index aa12cef615..5dc36e6358 100644 --- a/libs/renderengine/skia/filters/MouriMap.cpp +++ b/libs/renderengine/skia/filters/MouriMap.cpp @@ -104,7 +104,7 @@ sk_sp<SkImage> makeImage(SkSurface* surface, const SkRuntimeShaderBuilder& build paint.setShader(std::move(shader)); paint.setBlendMode(SkBlendMode::kSrc); surface->getCanvas()->drawPaint(paint); - return surface->makeImageSnapshot(); + return surface->makeTemporaryImage(); } } // namespace diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index ea5605d4bd..67f4aa10f8 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -23,6 +23,7 @@ #include <future> #include <android-base/stringprintf.h> +#include <common/FlagManager.h> #include <common/trace.h> #include <private/gui/SyncFeatures.h> #include <processgroup/processgroup.h> @@ -60,7 +61,7 @@ status_t RenderEngineThreaded::setSchedFifo(bool enabled) { struct sched_param param = {0}; int sched_policy; - if (enabled) { + if (enabled && !FlagManager::getInstance().disable_sched_fifo_re()) { sched_policy = SCHED_FIFO; param.sched_priority = kFifoPriority; } else { diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS index 7347ac74e6..4929b3fc92 100644 --- a/libs/sensor/OWNERS +++ b/libs/sensor/OWNERS @@ -1 +1 @@ -bduddie@google.com +include platform/frameworks/native:/services/sensorservice/OWNERS
\ No newline at end of file diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h index 4a6e849567..800bf3c1d4 100644 --- a/libs/tracing_perfetto/include/tracing_sdk.h +++ b/libs/tracing_perfetto/include/tracing_sdk.h @@ -27,6 +27,10 @@ #include "perfetto/public/te_macros.h" #include "perfetto/public/track_event.h" +#include "perfetto/public/abi/pb_decoder_abi.h" +#include "perfetto/public/pb_utils.h" +#include "perfetto/public/tracing_session.h" + /** * The objects declared here are intended to be managed by Java. * This means the Java Garbage Collector is responsible for freeing the @@ -452,6 +456,22 @@ class Proto { std::vector<PerfettoTeHlProtoField*> fields_; }; +class Session { + public: + Session(bool is_backend_in_process, void* buf, size_t len); + ~Session(); + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; + + bool FlushBlocking(uint32_t timeout_ms); + void StopBlocking(); + std::vector<uint8_t> ReadBlocking(); + + static void delete_session(Session* session); + + struct PerfettoTracingSessionImpl* session_ = nullptr; +}; + /** * @brief Activates a trigger. * @param name The name of the trigger. diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp index 0dab517b7f..79fb704906 100644 --- a/libs/tracing_perfetto/tests/Android.bp +++ b/libs/tracing_perfetto/tests/Android.bp @@ -21,44 +21,12 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library_static { - name: "libtracing_perfetto_test_utils", - export_include_dirs: [ - "include", - ], - static_libs: [ - "libflagtest", - "libgmock", - "perfetto_trace_protos", - ], - cflags: [ - "-Wall", - "-Werror", - "-Wno-enum-compare", - "-Wno-unused-function", - ], - - srcs: [ - "utils.cpp", - ], - - shared_libs: [ - "libperfetto_c", - "liblog", - "libprotobuf-cpp-lite", - ], - export_shared_lib_headers: [ - "libperfetto_c", - ], -} - cc_test { name: "libtracing_perfetto_tests", static_libs: [ "libflagtest", "libgmock", "perfetto_trace_protos", - "libtracing_perfetto_test_utils", ], cflags: [ "-Wall", @@ -68,12 +36,15 @@ cc_test { "android.os.flags-aconfig-cc-host", "libbase", "libperfetto_c", - "liblog", "libprotobuf-cpp-lite", "libtracing_perfetto", ], srcs: [ "tracing_perfetto_test.cpp", + "utils.cpp", + ], + local_include_dirs: [ + "include", ], test_suites: ["device-tests"], } diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp index 02e8d10aa4..c97e900952 100644 --- a/libs/tracing_perfetto/tracing_sdk.cpp +++ b/libs/tracing_perfetto/tracing_sdk.cpp @@ -254,6 +254,47 @@ const PerfettoTeHlProtoFieldNested* ProtoFieldNested::get() const { return &field_; } +Session::Session(bool is_backend_in_process, void* buf, size_t len) { + session_ = PerfettoTracingSessionCreate(is_backend_in_process + ? PERFETTO_BACKEND_IN_PROCESS + : PERFETTO_BACKEND_SYSTEM); + + PerfettoTracingSessionSetup(session_, buf, len); + + PerfettoTracingSessionStartBlocking(session_); +} + +Session::~Session() { + PerfettoTracingSessionStopBlocking(session_); + PerfettoTracingSessionDestroy(session_); +} + +bool Session::FlushBlocking(uint32_t timeout_ms) { + return PerfettoTracingSessionFlushBlocking(session_, timeout_ms); +} + +void Session::StopBlocking() { + PerfettoTracingSessionStopBlocking(session_); +} + +std::vector<uint8_t> Session::ReadBlocking() { + std::vector<uint8_t> data; + PerfettoTracingSessionReadTraceBlocking( + session_, + [](struct PerfettoTracingSessionImpl*, const void* trace_data, + size_t size, bool, void* user_arg) { + auto& dst = *static_cast<std::vector<uint8_t>*>(user_arg); + auto* src = static_cast<const uint8_t*>(trace_data); + dst.insert(dst.end(), src, src + size); + }, + &data); + return data; +} + +void Session::delete_session(Session* ptr) { + delete ptr; +} + void activate_trigger(const char* name, uint32_t ttl_ms) { const char* names[] = {name, nullptr}; PerfettoProducerActivateTriggers(names, ttl_ms); diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index c9f0761a01..b7ef9b3738 100644 --- a/libs/ui/DisplayIdentification.cpp +++ b/libs/ui/DisplayIdentification.cpp @@ -26,6 +26,7 @@ #include <string> #include <string_view> +#include <ftl/concat.h> #include <ftl/hash.h> #include <log/log.h> #include <ui/DisplayIdentification.h> @@ -392,10 +393,6 @@ std::optional<PnpId> getPnpId(uint16_t manufacturerId) { return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt; } -std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) { - return getPnpId(displayId.getManufacturerId()); -} - std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( uint8_t port, const DisplayIdentificationData& data) { if (data.empty()) { @@ -427,4 +424,27 @@ PhysicalDisplayId getVirtualDisplayId(uint32_t id) { return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id); } +PhysicalDisplayId generateEdidDisplayId(const Edid& edid) { + const ftl::Concat displayDetailsString{edid.manufacturerId, + edid.productId, + ftl::truncated<13>(edid.displayName), + edid.manufactureWeek, + edid.manufactureOrModelYear, + edid.physicalSizeInCm.getWidth(), + edid.physicalSizeInCm.getHeight()}; + + // String has to be cropped to 64 characters (at most) for ftl::stable_hash. + // This is fine as the accuracy or completeness of the above fields is not + // critical for a ID fabrication. + const std::optional<uint64_t> hashedDisplayDetailsOpt = + ftl::stable_hash(std::string_view(displayDetailsString.c_str(), 64)); + + // Combine the hashes via bit-shifted XORs. + const uint64_t id = (hashedDisplayDetailsOpt.value_or(0) << 17) ^ + (edid.hashedBlockZeroSerialNumberOpt.value_or(0) >> 11) ^ + (edid.hashedDescriptorBlockSerialNumberOpt.value_or(0) << 23); + + return PhysicalDisplayId::fromEdidHash(id); +} + } // namespace android diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h index 8a14db8ffa..13ed754534 100644 --- a/libs/ui/include/ui/DisplayId.h +++ b/libs/ui/include/ui/DisplayId.h @@ -20,7 +20,6 @@ #include <ostream> #include <string> -#include <ftl/hash.h> #include <ftl/optional.h> namespace android { @@ -31,16 +30,12 @@ struct DisplayId { // Flag indicating that the display is virtual. static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63; - // Flag indicating that the ID is stable across reboots. - static constexpr uint64_t FLAG_STABLE = 1ULL << 62; - // TODO(b/162612135) Remove default constructor DisplayId() = default; constexpr DisplayId(const DisplayId&) = default; DisplayId& operator=(const DisplayId&) = default; constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; } - constexpr bool isStable() const { return value & FLAG_STABLE; } uint64_t value; @@ -86,12 +81,17 @@ struct PhysicalDisplayId : DisplayId { return PhysicalDisplayId(id); } - // Returns a stable ID based on EDID information. + // Returns a stable ID based on EDID and port information. static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) { return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash); } + // Returns a stable and consistent ID based exclusively on EDID information. + static constexpr PhysicalDisplayId fromEdidHash(uint64_t hashedEdid) { + return PhysicalDisplayId(hashedEdid); + } + // Returns an unstable ID. If EDID is available using "fromEdid" is preferred. static constexpr PhysicalDisplayId fromPort(uint8_t port) { constexpr uint16_t kManufacturerId = 0; @@ -102,10 +102,14 @@ struct PhysicalDisplayId : DisplayId { // TODO(b/162612135) Remove default constructor PhysicalDisplayId() = default; - constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); } constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); } private: + // Flag indicating that the ID is stable across reboots. + static constexpr uint64_t FLAG_STABLE = 1ULL << 62; + + using DisplayId::DisplayId; + constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId, uint32_t modelHash) : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) | @@ -149,13 +153,6 @@ private: struct GpuVirtualDisplayId : VirtualDisplayId { explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {} - static constexpr std::optional<GpuVirtualDisplayId> fromUniqueId(const std::string& uniqueId) { - if (const auto hashOpt = ftl::stable_hash(uniqueId)) { - return GpuVirtualDisplayId(HashTag{}, *hashOpt); - } - return {}; - } - static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) { if (id.isVirtual() && (id.value & FLAG_GPU)) { return GpuVirtualDisplayId(id); @@ -164,10 +161,6 @@ struct GpuVirtualDisplayId : VirtualDisplayId { } private: - struct HashTag {}; // Disambiguate with BaseId constructor. - constexpr GpuVirtualDisplayId(HashTag, uint64_t hash) - : VirtualDisplayId(FLAG_STABLE | FLAG_GPU | hash) {} - explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {} }; diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index cdac2698dd..1e3449c004 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -74,6 +74,7 @@ struct Edid { std::optional<uint64_t> hashedDescriptorBlockSerialNumberOpt; PnpId pnpId; uint32_t modelHash; + // Up to 13 characters of ASCII text terminated by LF and padded with SP. std::string_view displayName; uint8_t manufactureOrModelYear; uint8_t manufactureWeek; @@ -85,11 +86,14 @@ struct Edid { bool isEdid(const DisplayIdentificationData&); std::optional<Edid> parseEdid(const DisplayIdentificationData&); std::optional<PnpId> getPnpId(uint16_t manufacturerId); -std::optional<PnpId> getPnpId(PhysicalDisplayId); std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( uint8_t port, const DisplayIdentificationData&); PhysicalDisplayId getVirtualDisplayId(uint32_t id); +// Generates a consistent, stable, and hashed display ID that is based on the +// display's parsed EDID fields. +PhysicalDisplayId generateEdidDisplayId(const Edid& edid); + } // namespace android diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp index ef686dfc83..090d2eefc3 100644 --- a/libs/ui/tests/DisplayId_test.cpp +++ b/libs/ui/tests/DisplayId_test.cpp @@ -26,7 +26,6 @@ TEST(DisplayIdTest, createPhysicalIdFromEdid) { constexpr uint32_t modelHash = 42; const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash); EXPECT_EQ(port, id.getPort()); - EXPECT_EQ(manufacturerId, id.getManufacturerId()); EXPECT_FALSE(VirtualDisplayId::tryCast(id)); EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); @@ -75,21 +74,6 @@ TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) { EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value()); } -TEST(DisplayIdTest, createGpuVirtualIdFromUniqueId) { - static const std::string kUniqueId("virtual:ui:DisplayId_test"); - const auto idOpt = GpuVirtualDisplayId::fromUniqueId(kUniqueId); - ASSERT_TRUE(idOpt.has_value()); - const GpuVirtualDisplayId id = idOpt.value(); - EXPECT_TRUE(VirtualDisplayId::tryCast(id)); - EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); - EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); - EXPECT_FALSE(HalDisplayId::tryCast(id)); - - EXPECT_EQ(id, DisplayId::fromValue(id.value)); - EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value)); -} - TEST(DisplayIdTest, createHalVirtualId) { const HalVirtualDisplayId id(42); EXPECT_TRUE(VirtualDisplayId::tryCast(id)); diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp index d1699e79b6..75c71a598e 100644 --- a/libs/ui/tests/DisplayIdentification_test.cpp +++ b/libs/ui/tests/DisplayIdentification_test.cpp @@ -376,6 +376,22 @@ TEST(DisplayIdentificationTest, parseDisplayIdentificationData) { EXPECT_EQ(4633127902230889474, tertiaryInfo->id.value); } +TEST(DisplayIdentificationTest, generateEdidDisplayId) { + const auto firstExternalDisplayEdidOpt = parseEdid(getExternalEdid()); + ASSERT_TRUE(firstExternalDisplayEdidOpt); + const PhysicalDisplayId firstExternalDisplayId = + generateEdidDisplayId(firstExternalDisplayEdidOpt.value()); + + const auto secondExternalDisplayEdidOpt = parseEdid(getExternalEedid()); + ASSERT_TRUE(secondExternalDisplayEdidOpt); + const PhysicalDisplayId secondExternalDisplayId = + generateEdidDisplayId(secondExternalDisplayEdidOpt.value()); + + // Display IDs should be unique. + EXPECT_EQ(4067182673952280501u, firstExternalDisplayId.value); + EXPECT_EQ(14712168404707886855u, secondExternalDisplayId.value); +} + TEST(DisplayIdentificationTest, deviceProductInfo) { using ManufactureYear = DeviceProductInfo::ManufactureYear; using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear; @@ -462,18 +478,6 @@ TEST(DisplayIdentificationTest, deviceProductInfo) { } } -TEST(DisplayIdentificationTest, fromPort) { - // Manufacturer ID should be invalid. - ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0))); - ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0xffu))); -} - -TEST(DisplayIdentificationTest, getVirtualDisplayId) { - // Manufacturer ID should be invalid. - ASSERT_FALSE(getPnpId(getVirtualDisplayId(0))); - ASSERT_FALSE(getPnpId(getVirtualDisplayId(0xffff'ffffu))); -} - } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index 91250b9945..eb747c767e 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -8,6 +8,11 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +cc_aconfig_library { + name: "libegl_flags_c_lib", + aconfig_declarations: "graphicsenv_flags", +} + cc_library { name: "libETC1", srcs: ["ETC1/etc1.cpp"], @@ -155,7 +160,10 @@ cc_library_static { cc_library_shared { name: "libEGL", - defaults: ["egl_libs_defaults"], + defaults: [ + "aconfig_lib_cc_static_link.defaults", + "egl_libs_defaults", + ], llndk: { symbol_file: "libEGL.map.txt", export_llndk_headers: ["gl_headers"], @@ -191,6 +199,7 @@ cc_library_shared { static_libs: [ "libEGL_getProcAddress", "libEGL_blobCache", + "libegl_flags_c_lib", ], ldflags: [ "-Wl,--exclude-libs=libEGL_getProcAddress.a", diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index b1a287fcec..5fe948478b 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -22,6 +22,7 @@ #include <android-base/properties.h> #include <android/dlext.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <com_android_graphics_graphicsenv_flags.h> #include <configstore/Utils.h> #include <dlfcn.h> #include <graphicsenv/GraphicsEnv.h> @@ -37,6 +38,7 @@ using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; +namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags; namespace android { @@ -138,15 +140,40 @@ static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_conn attrs.push_back(attr[1]); } } - const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures(); - std::vector<const char*> features; - if (eglFeatures.size() > 0) { + + if (graphicsenv_flags::feature_overrides()) { + std::vector<const char*> enabled; // ANGLE features to enable + std::vector<const char*> disabled; // ANGLE features to disable + + // Get the list of ANGLE features to enable from Global.Settings. + const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures(); for (const std::string& eglFeature : eglFeatures) { - features.push_back(eglFeature.c_str()); + enabled.push_back(eglFeature.c_str()); + } + + // Get the list of ANGLE features to enable/disable from gpuservice. + GraphicsEnv::getInstance().getAngleFeatureOverrides(enabled, disabled); + if (!enabled.empty()) { + enabled.push_back(0); + attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE); + attrs.push_back(reinterpret_cast<EGLAttrib>(enabled.data())); + } + if (!disabled.empty()) { + disabled.push_back(0); + attrs.push_back(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE); + attrs.push_back(reinterpret_cast<EGLAttrib>(disabled.data())); + } + } else { + const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures(); + std::vector<const char*> features; + if (eglFeatures.size() > 0) { + for (const std::string& eglFeature : eglFeatures) { + features.push_back(eglFeature.c_str()); + } + features.push_back(0); + attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE); + attrs.push_back(reinterpret_cast<EGLAttrib>(features.data())); } - features.push_back(0); - attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE); - attrs.push_back(reinterpret_cast<EGLAttrib>(features.data())); } attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp index 99360b9a14..8db9a78f7f 100644 --- a/services/audiomanager/IAudioManager.cpp +++ b/services/audiomanager/IAudioManager.cpp @@ -35,11 +35,11 @@ public: { } - // This should never fail virtual sp<media::IAudioManagerNative> getNativeInterface() { Parcel data, reply; data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor()); const status_t res = remote()->transact(GET_NATIVE_INTERFACE, data, &reply, 0); + if (res == DEAD_OBJECT) return nullptr; LOG_ALWAYS_FATAL_IF(res != OK, "%s failed with result %d", __func__, res); const int ex = reply.readExceptionCode(); LOG_ALWAYS_FATAL_IF(ex != binder::Status::EX_NONE, "%s failed with exception %d", @@ -48,7 +48,7 @@ public: sp<IBinder> binder; const status_t err = reply.readNullableStrongBinder(&binder); LOG_ALWAYS_FATAL_IF(binder == nullptr, "%s failed unexpected nullptr %d", __func__, err); - const auto iface = checked_interface_cast<media::IAudioManagerNative>(std::move(binder)); + const auto iface = checked_interface_cast<media::IAudioManagerNative>(binder); LOG_ALWAYS_FATAL_IF(iface == nullptr, "%s failed unexpected interface", __func__); return iface; } diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index ca9fe5ea43..689221f4af 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -19,10 +19,16 @@ cc_defaults { ], } +cc_aconfig_library { + name: "gpuservice_flags_c_lib", + aconfig_declarations: "graphicsenv_flags", +} + cc_defaults { name: "libgpuservice_defaults", defaults: [ "gpuservice_defaults", + "libfeatureoverride_deps", "libgfxstats_deps", "libgpumem_deps", "libgpumemtracer_deps", @@ -40,8 +46,11 @@ cc_defaults { "libgraphicsenv", "liblog", "libutils", + "server_configurable_flags", ], static_libs: [ + "gpuservice_flags_c_lib", + "libfeatureoverride", "libgfxstats", "libgpumem", "libgpumemtracer", diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS index 07c681f1f4..a3afca5c1a 100644 --- a/services/gpuservice/OWNERS +++ b/services/gpuservice/OWNERS @@ -1,7 +1,4 @@ chrisforbes@google.com -lpy@google.com -alecmouri@google.com -lfy@google.com -paulthomson@google.com -pbaiget@google.com -kocdemir@google.com +tomnom@google.com + +alecmouri@google.com #{LAST_RESORT_SUGGESTION} diff --git a/services/gpuservice/feature_override/Android.bp b/services/gpuservice/feature_override/Android.bp new file mode 100644 index 0000000000..cda67f0501 --- /dev/null +++ b/services/gpuservice/feature_override/Android.bp @@ -0,0 +1,98 @@ +// Copyright 2024 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. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_defaults { + name: "libfeatureoverride_deps", + include_dirs: [ + "external/protobuf", + "external/protobuf/src", + ], + header_libs: [ + "libbase_headers", + ], + shared_libs: [ + "libbase", + "libgraphicsenv", + "liblog", + ], + static_libs: [ + "libprotobuf-cpp-lite-ndk", + ], +} + +filegroup { + name: "feature_config_proto_definitions", + srcs: [ + "proto/feature_config.proto", + ], +} + +genrule { + name: "feature_config_proto_lite_gen_headers", + srcs: [ + ":feature_config_proto_definitions", + ], + tools: [ + "aprotoc", + ], + cmd: "$(location aprotoc) " + + "--proto_path=frameworks/native/services/gpuservice/feature_override " + + "--cpp_out=lite=true:$(genDir)/frameworks/native/services/gpuservice/feature_override " + + "$(locations :feature_config_proto_definitions)", + out: [ + "frameworks/native/services/gpuservice/feature_override/proto/feature_config.pb.h", + ], + export_include_dirs: [ + "frameworks/native/services/gpuservice/feature_override/proto/", + ], +} + +cc_library_static { + name: "libfeatureoverride", + defaults: [ + "libfeatureoverride_deps", + ], + srcs: [ + ":feature_config_proto_definitions", + "FeatureOverrideParser.cpp", + ], + local_include_dirs: [ + "include", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wimplicit-fallthrough", + ], + cppflags: [ + "-Wno-sign-compare", + ], + export_include_dirs: ["include"], + proto: { + type: "lite", + static: true, + }, + generated_headers: [ + "feature_config_proto_lite_gen_headers", + ], +} diff --git a/services/gpuservice/feature_override/FeatureOverrideParser.cpp b/services/gpuservice/feature_override/FeatureOverrideParser.cpp new file mode 100644 index 0000000000..1ad637ce35 --- /dev/null +++ b/services/gpuservice/feature_override/FeatureOverrideParser.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2025 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 <feature_override/FeatureOverrideParser.h> + +#include <chrono> +#include <fstream> +#include <sstream> +#include <string> +#include <sys/stat.h> +#include <vector> + +#include <graphicsenv/FeatureOverrides.h> +#include <log/log.h> + +#include "feature_config.pb.h" + +namespace { + +void resetFeatureOverrides(android::FeatureOverrides &featureOverrides) { + featureOverrides.mGlobalFeatures.clear(); + featureOverrides.mPackageFeatures.clear(); +} + +void initFeatureConfig(android::FeatureConfig &featureConfig, + const feature_override::FeatureConfig &featureConfigProto) { + featureConfig.mFeatureName = featureConfigProto.feature_name(); + featureConfig.mEnabled = featureConfigProto.enabled(); +} + +feature_override::FeatureOverrideProtos readFeatureConfigProtos(std::string configFilePath) { + feature_override::FeatureOverrideProtos overridesProtos; + + std::ifstream protobufBinaryFile(configFilePath.c_str()); + if (protobufBinaryFile.fail()) { + ALOGE("Failed to open feature config file: `%s`.", configFilePath.c_str()); + return overridesProtos; + } + + std::stringstream buffer; + buffer << protobufBinaryFile.rdbuf(); + std::string serializedConfig = buffer.str(); + std::vector<uint8_t> serialized( + reinterpret_cast<const uint8_t *>(serializedConfig.data()), + reinterpret_cast<const uint8_t *>(serializedConfig.data()) + + serializedConfig.size()); + + if (!overridesProtos.ParseFromArray(serialized.data(), + static_cast<int>(serialized.size()))) { + ALOGE("Failed to parse GpuConfig protobuf data."); + } + + return overridesProtos; +} + +} // namespace + +namespace android { + +std::string FeatureOverrideParser::getFeatureOverrideFilePath() const { + const std::string kConfigFilePath = "/system/etc/angle/feature_config_vk.binarypb"; + + return kConfigFilePath; +} + +bool FeatureOverrideParser::shouldReloadFeatureOverrides() const { + std::string configFilePath = getFeatureOverrideFilePath(); + struct stat fileStat{}; + if (stat(getFeatureOverrideFilePath().c_str(), &fileStat) != 0) { + ALOGE("Error getting file information for '%s': %s", getFeatureOverrideFilePath().c_str(), + strerror(errno)); + // stat'ing the file failed, so return false since reading it will also likely fail. + return false; + } + + return fileStat.st_mtime > mLastProtobufReadTime; +} + +void FeatureOverrideParser::forceFileRead() { + resetFeatureOverrides(mFeatureOverrides); + mLastProtobufReadTime = 0; +} + +void FeatureOverrideParser::parseFeatureOverrides() { + const feature_override::FeatureOverrideProtos overridesProtos = readFeatureConfigProtos( + getFeatureOverrideFilePath()); + + // Global feature overrides. + for (const auto &featureConfigProto: overridesProtos.global_features()) { + FeatureConfig featureConfig; + initFeatureConfig(featureConfig, featureConfigProto); + + mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig); + } + + // App-specific feature overrides. + for (auto const &pkgConfigProto: overridesProtos.package_features()) { + const std::string &packageName = pkgConfigProto.package_name(); + + if (mFeatureOverrides.mPackageFeatures.count(packageName)) { + ALOGE("Package already has feature overrides! Skipping."); + continue; + } + + std::vector<FeatureConfig> featureConfigs; + for (const auto &featureConfigProto: pkgConfigProto.feature_configs()) { + FeatureConfig featureConfig; + initFeatureConfig(featureConfig, featureConfigProto); + + featureConfigs.emplace_back(featureConfig); + } + + mFeatureOverrides.mPackageFeatures[packageName] = featureConfigs; + } + + mLastProtobufReadTime = std::chrono::system_clock::to_time_t( + std::chrono::system_clock::now()); +} + +FeatureOverrides FeatureOverrideParser::getFeatureOverrides() { + if (shouldReloadFeatureOverrides()) { + parseFeatureOverrides(); + } + + return mFeatureOverrides; +} + +} // namespace android diff --git a/services/gpuservice/feature_override/include/feature_override/FeatureOverrideParser.h b/services/gpuservice/feature_override/include/feature_override/FeatureOverrideParser.h new file mode 100644 index 0000000000..b1f1867d99 --- /dev/null +++ b/services/gpuservice/feature_override/include/feature_override/FeatureOverrideParser.h @@ -0,0 +1,49 @@ +/* + * Copyright 2024 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. + */ + +#ifndef FEATURE_OVERRIDE_PARSER_H_ +#define FEATURE_OVERRIDE_PARSER_H_ + +#include <ctime> +#include <string> +#include <vector> + +#include <graphicsenv/FeatureOverrides.h> + +namespace android { + +class FeatureOverrideParser { +public: + FeatureOverrideParser() = default; + FeatureOverrideParser(const FeatureOverrideParser &) = default; + virtual ~FeatureOverrideParser() = default; + + FeatureOverrides getFeatureOverrides(); + void forceFileRead(); + +private: + bool shouldReloadFeatureOverrides() const; + void parseFeatureOverrides(); + // Allow FeatureOverrideParserMock to override with the unit test file's path. + virtual std::string getFeatureOverrideFilePath() const; + + std::time_t mLastProtobufReadTime = 0; + FeatureOverrides mFeatureOverrides; +}; + +} // namespace android + +#endif // FEATURE_OVERRIDE_PARSER_H_ diff --git a/services/gpuservice/feature_override/proto/feature_config.proto b/services/gpuservice/feature_override/proto/feature_config.proto new file mode 100644 index 0000000000..4d4bf289f1 --- /dev/null +++ b/services/gpuservice/feature_override/proto/feature_config.proto @@ -0,0 +1,53 @@ +/* + * Copyright 2024 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. + */ + +syntax = "proto3"; + +package feature_override; + +option optimize_for = LITE_RUNTIME; + +/** + * Feature Configuration + * feature_name: Feature name (see external/angle/include/platform/autogen/FeaturesVk_autogen.h). + * enabled: Either enable or disable the feature. + */ +message FeatureConfig +{ + string feature_name = 1; + bool enabled = 2; +} + +/** + * Package Configuration + * feature_configs: List of features configs for the package. + */ +message PackageConfig +{ + string package_name = 1; + repeated FeatureConfig feature_configs = 2; +} + +/** + * Feature Overrides + * global_features: Features to apply globally, for every package. + * package_features: Features to apply for individual packages. + */ +message FeatureOverrideProtos +{ + repeated FeatureConfig global_features = 1; + repeated PackageConfig package_features = 2; +} diff --git a/services/gpuservice/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h index 3072885838..057d127390 100644 --- a/services/gpuservice/include/gpuservice/GpuService.h +++ b/services/gpuservice/include/gpuservice/GpuService.h @@ -19,6 +19,7 @@ #include <binder/IInterface.h> #include <cutils/compiler.h> +#include <feature_override/FeatureOverrideParser.h> #include <graphicsenv/GpuStatsInfo.h> #include <graphicsenv/IGpuService.h> #include <serviceutils/PriorityDumper.h> @@ -96,6 +97,7 @@ private: std::string mDeveloperDriverPath; std::unique_ptr<std::thread> mGpuMemAsyncInitThread; std::unique_ptr<std::thread> mGpuWorkAsyncInitThread; + FeatureOverrideParser mFeatureOverrideParser; }; } // namespace android diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp index 8056a2c601..d2184d863b 100644 --- a/services/gpuservice/tests/unittests/Android.bp +++ b/services/gpuservice/tests/unittests/Android.bp @@ -21,17 +21,71 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +cc_aconfig_library { + name: "gpuservice_unittest_flags_c_lib", + aconfig_declarations: "graphicsenv_flags", +} + +genrule_defaults { + name: "gpuservice_unittest_feature_config_pb_defaults", + tools: ["aprotoc"], + tool_files: [ + ":feature_config_proto_definitions", + ], + cmd: "$(location aprotoc) " + + "--encode=feature_override.FeatureOverrideProtos " + + "$(locations :feature_config_proto_definitions) " + + "< $(in) " + + "> $(out) ", +} + +// Main protobuf used by the unit tests. +filegroup { + name: "gpuservice_unittest_feature_config_vk_prototext", + srcs: [ + "data/feature_config_test.txtpb", + ], +} + +genrule { + name: "gpuservice_unittest_feature_config_vk_binarypb", + defaults: ["gpuservice_unittest_feature_config_pb_defaults"], + srcs: [ + ":gpuservice_unittest_feature_config_vk_prototext", + ], + out: ["gpuservice_unittest_feature_config_vk.binarypb"], +} + +// "Updated" protobuf, used to validate forceFileRead(). +filegroup { + name: "gpuservice_unittest_feature_config_vk_force_read_prototext", + srcs: [ + "data/feature_config_test_force_read.txtpb", + ], +} + +genrule { + name: "gpuservice_unittest_feature_config_vk_force_read_binarypb", + defaults: ["gpuservice_unittest_feature_config_pb_defaults"], + srcs: [ + ":gpuservice_unittest_feature_config_vk_force_read_prototext", + ], + out: ["gpuservice_unittest_feature_config_vk_force_read.binarypb"], +} + cc_test { name: "gpuservice_unittest", test_suites: ["device-tests"], defaults: [ + "aconfig_lib_cc_static_link.defaults", "libgpuservice_defaults", ], srcs: [ + "FeatureOverrideParserTest.cpp", "GpuMemTest.cpp", "GpuMemTracerTest.cpp", - "GpuStatsTest.cpp", "GpuServiceTest.cpp", + "GpuStatsTest.cpp", ], header_libs: ["bpf_headers"], shared_libs: [ @@ -48,10 +102,15 @@ cc_test { "libutils", ], static_libs: [ + "gpuservice_unittest_flags_c_lib", "libgmock", "libgpuservice", "libperfetto_client_experimental", "perfetto_trace_protos", ], + data: [ + ":gpuservice_unittest_feature_config_vk_binarypb", + ":gpuservice_unittest_feature_config_vk_force_read_binarypb", + ], require_root: true, } diff --git a/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp new file mode 100644 index 0000000000..65a1b58dc3 --- /dev/null +++ b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2025 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. + */ + +#undef LOG_TAG +#define LOG_TAG "gpuservice_unittest" + +#include <android-base/file.h> +#include <log/log.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <com_android_graphics_graphicsenv_flags.h> +#include <feature_override/FeatureOverrideParser.h> + +using ::testing::AtLeast; +using ::testing::Return; + +namespace android { +namespace { + +std::string getTestBinarypbPath(const std::string &filename) { + std::string path = android::base::GetExecutableDirectory(); + path.append("/"); + path.append(filename); + + return path; +} + +class FeatureOverrideParserMock : public FeatureOverrideParser { +public: + MOCK_METHOD(std::string, getFeatureOverrideFilePath, (), (const, override)); +}; + +class FeatureOverrideParserTest : public testing::Test { +public: + FeatureOverrideParserTest() { + const ::testing::TestInfo *const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + ~FeatureOverrideParserTest() { + const ::testing::TestInfo *const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), + test_info->name()); + } + + void SetUp() override { + const std::string filename = "gpuservice_unittest_feature_config_vk.binarypb"; + + EXPECT_CALL(mFeatureOverrideParser, getFeatureOverrideFilePath()) + .WillRepeatedly(Return(getTestBinarypbPath(filename))); + } + + FeatureOverrideParserMock mFeatureOverrideParser; +}; + +testing::AssertionResult validateFeatureConfigTestTxtpbSizes(FeatureOverrides overrides) { + size_t expectedGlobalFeaturesSize = 1; + if (overrides.mGlobalFeatures.size() != expectedGlobalFeaturesSize) { + return testing::AssertionFailure() + << "overrides.mGlobalFeatures.size(): " << overrides.mGlobalFeatures.size() + << ", expected: " << expectedGlobalFeaturesSize; + } + + size_t expectedPackageFeaturesSize = 1; + if (overrides.mPackageFeatures.size() != expectedPackageFeaturesSize) { + return testing::AssertionFailure() + << "overrides.mPackageFeatures.size(): " << overrides.mPackageFeatures.size() + << ", expected: " << expectedPackageFeaturesSize; + } + + return testing::AssertionSuccess(); +} + +testing::AssertionResult validateFeatureConfigTestForceReadTxtpbSizes(FeatureOverrides overrides) { + size_t expectedGlobalFeaturesSize = 1; + if (overrides.mGlobalFeatures.size() != expectedGlobalFeaturesSize) { + return testing::AssertionFailure() + << "overrides.mGlobalFeatures.size(): " << overrides.mGlobalFeatures.size() + << ", expected: " << expectedGlobalFeaturesSize; + } + + size_t expectedPackageFeaturesSize = 0; + if (overrides.mPackageFeatures.size() != expectedPackageFeaturesSize) { + return testing::AssertionFailure() + << "overrides.mPackageFeatures.size(): " << overrides.mPackageFeatures.size() + << ", expected: " << expectedPackageFeaturesSize; + } + + return testing::AssertionSuccess(); +} + +testing::AssertionResult validateGlobalOverrides1(FeatureOverrides overrides) { + const int kTestFeatureIndex = 0; + const std::string expectedFeatureName = "globalOverrides1"; + const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex]; + + if (cfg.mFeatureName != expectedFeatureName) { + return testing::AssertionFailure() + << "cfg.mFeatureName: " << cfg.mFeatureName + << ", expected: " << expectedFeatureName; + } + + bool expectedEnabled = false; + if (cfg.mEnabled != expectedEnabled) { + return testing::AssertionFailure() + << "cfg.mEnabled: " << cfg.mEnabled + << ", expected: " << expectedEnabled; + } + + return testing::AssertionSuccess(); +} + +TEST_F(FeatureOverrideParserTest, globalOverrides1) { + FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides(); + + EXPECT_TRUE(validateFeatureConfigTestTxtpbSizes(overrides)); + EXPECT_TRUE(validateGlobalOverrides1(overrides)); +} + +testing::AssertionResult validatePackageOverrides1(FeatureOverrides overrides) { + const std::string expectedTestPackageName = "com.gpuservice_unittest.packageOverrides1"; + + if (!overrides.mPackageFeatures.count(expectedTestPackageName)) { + return testing::AssertionFailure() + << "overrides.mPackageFeatures missing expected package: " + << expectedTestPackageName; + } + + const std::vector<FeatureConfig>& features = + overrides.mPackageFeatures[expectedTestPackageName]; + + size_t expectedFeaturesSize = 1; + if (features.size() != expectedFeaturesSize) { + return testing::AssertionFailure() + << "features.size(): " << features.size() + << ", expectedFeaturesSize: " << expectedFeaturesSize; + } + + const std::string expectedFeatureName = "packageOverrides1"; + const FeatureConfig &cfg = features[0]; + + bool expectedEnabled = true; + if (cfg.mEnabled != expectedEnabled) { + return testing::AssertionFailure() + << "cfg.mEnabled: " << cfg.mEnabled + << ", expected: " << expectedEnabled; + } + + return testing::AssertionSuccess(); +} + +TEST_F(FeatureOverrideParserTest, packageOverrides1) { + FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides(); + + EXPECT_TRUE(validateFeatureConfigTestTxtpbSizes(overrides)); + EXPECT_TRUE(validatePackageOverrides1(overrides)); +} + +testing::AssertionResult validateForceFileRead(FeatureOverrides overrides) { + const int kTestFeatureIndex = 0; + const std::string expectedFeatureName = "forceFileRead"; + + const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex]; + if (cfg.mFeatureName != expectedFeatureName) { + return testing::AssertionFailure() + << "cfg.mFeatureName: " << cfg.mFeatureName + << ", expected: " << expectedFeatureName; + } + + bool expectedEnabled = false; + if (cfg.mEnabled != expectedEnabled) { + return testing::AssertionFailure() + << "cfg.mEnabled: " << cfg.mEnabled + << ", expected: " << expectedEnabled; + } + + return testing::AssertionSuccess(); +} + +TEST_F(FeatureOverrideParserTest, forceFileRead) { + FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides(); + + // Validate the "original" contents are present. + EXPECT_TRUE(validateFeatureConfigTestTxtpbSizes(overrides)); + EXPECT_TRUE(validateGlobalOverrides1(overrides)); + + // "Update" the config file. + const std::string filename = "gpuservice_unittest_feature_config_vk_force_read.binarypb"; + EXPECT_CALL(mFeatureOverrideParser, getFeatureOverrideFilePath()) + .WillRepeatedly(Return(getTestBinarypbPath(filename))); + + mFeatureOverrideParser.forceFileRead(); + + overrides = mFeatureOverrideParser.getFeatureOverrides(); + + // Validate the new file contents were read and parsed. + EXPECT_TRUE(validateFeatureConfigTestForceReadTxtpbSizes(overrides)); + EXPECT_TRUE(validateForceFileRead(overrides)); +} + +} // namespace +} // namespace android diff --git a/services/gpuservice/tests/unittests/data/feature_config_test.txtpb b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb new file mode 100644 index 0000000000..726779e332 --- /dev/null +++ b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb @@ -0,0 +1,40 @@ +# Copyright (C) 2024 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. +# +# Feature Configuration Test Data +# +# proto-file: services/gpuservice/feature_override/proto/feature_config.proto +# proto-message: FeatureOverrideProtos + +# The 'feature_name' entries correspond to the FeatureOverrideParserTest() unit test name. +global_features [ + { + feature_name: "globalOverrides1" + enabled: False + } +] + +# The 'package_name' and 'feature_name' entries correspond to the +# FeatureOverrideParserTest() unit test name. +package_features [ + { + package_name: "com.gpuservice_unittest.packageOverrides1" + feature_configs: [ + { + feature_name: "packageOverrides1" + enabled: True + } + ] + } +] diff --git a/services/gpuservice/tests/unittests/data/feature_config_test_force_read.txtpb b/services/gpuservice/tests/unittests/data/feature_config_test_force_read.txtpb new file mode 100644 index 0000000000..cf6a67e314 --- /dev/null +++ b/services/gpuservice/tests/unittests/data/feature_config_test_force_read.txtpb @@ -0,0 +1,26 @@ +# Copyright (C) 2024 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. +# +# Feature Configuration Test Data +# +# proto-file: services/gpuservice/feature_override/proto/feature_config.proto +# proto-message: FeatureOverrideProtos + +# The 'feature_name' entries correspond to the FeatureOverrideParserTest() unit test name. +global_features [ + { + feature_name: "forceFileRead" + enabled: False + } +] diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index b155122749..7d62ed9461 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -41,8 +41,6 @@ namespace { const bool ENABLE_INPUT_DEVICE_USAGE_METRICS = sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true); -const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl(); - int32_t exceptionCodeFromStatusT(status_t status) { switch (status) { case OK: @@ -134,12 +132,10 @@ InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, mTracingStages.emplace_back( std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher)); - if (ENABLE_INPUT_FILTER_RUST) { - mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust, - inputFilterPolicy); - mTracingStages.emplace_back( - std::make_unique<TracedInputListener>("InputFilter", *mInputFilter)); - } + mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust, + inputFilterPolicy); + mTracingStages.emplace_back( + std::make_unique<TracedInputListener>("InputFilter", *mInputFilter)); if (ENABLE_INPUT_DEVICE_USAGE_METRICS) { mCollector = std::make_unique<InputDeviceMetricsCollector>(*mTracingStages.back()); @@ -250,10 +246,8 @@ void InputManager::dump(std::string& dump) { mCollector->dump(dump); dump += '\n'; } - if (ENABLE_INPUT_FILTER_RUST) { - mInputFilter->dump(dump); - dump += '\n'; - } + mInputFilter->dump(dump); + dump += '\n'; mDispatcher->dump(dump); dump += '\n'; } diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 5bf6ebbb46..9f9128529d 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -1017,9 +1017,9 @@ PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId so sourceBoundary == DisplayTopologyPosition::BOTTOM ? (destinationViewport->logicalRight - destinationViewport->logicalLeft) : (destinationViewport->logicalBottom - destinationViewport->logicalTop); - if (cursorOffset >= adjacentDisplay.offsetPx && - cursorOffset <= adjacentDisplay.offsetPx + edgeSize) { - return std::make_pair(destinationViewport, adjacentDisplay.offsetPx); + if (cursorOffset >= adjacentDisplay.offsetDp && + cursorOffset <= adjacentDisplay.offsetDp + edgeSize) { + return std::make_pair(destinationViewport, adjacentDisplay.offsetDp); } } return std::nullopt; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 717d26c4c7..493e480d1d 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -918,6 +918,21 @@ InputTarget createInputTarget(const std::shared_ptr<Connection>& connection, return inputTarget; } +std::string dumpWindowForTouchOcclusion(const WindowInfo& info, bool isTouchedWindow) { + return StringPrintf(INDENT2 "* %spackage=%s/%s, id=%" PRId32 ", mode=%s, alpha=%.2f, " + "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32 + "], touchableRegion=%s, window={%s}, inputConfig={%s}, " + "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n", + isTouchedWindow ? "[TOUCHED] " : "", info.packageName.c_str(), + info.ownerUid.toString().c_str(), info.id, + toString(info.touchOcclusionMode).c_str(), info.alpha, info.frame.left, + info.frame.top, info.frame.right, info.frame.bottom, + dumpRegion(info.touchableRegion).c_str(), info.name.c_str(), + info.inputConfig.string().c_str(), toString(info.token != nullptr), + info.applicationInfo.name.c_str(), + binderToString(info.applicationInfo.token).c_str()); +} + } // namespace // --- InputDispatcher --- @@ -928,16 +943,17 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy) InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, std::unique_ptr<trace::InputTracingBackendInterface> traceBackend) : mPolicy(policy), + mLooper(sp<Looper>::make(false)), mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL), + mConnectionManager(mLooper), mNextUnblockedEvent(nullptr), mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), - mMaximumObscuringOpacityForTouch(1.0f), mFocusedDisplayId(ui::LogicalDisplayId::DEFAULT), mWindowTokenWithPointerCapture(nullptr), mAwaitedApplicationDisplayId(ui::LogicalDisplayId::INVALID), @@ -948,7 +964,6 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, : std::move(std::unique_ptr<InputEventTimelineProcessor>( new LatencyAggregator()))), mLatencyTracker(*mInputEventTimelineProcessor) { - mLooper = sp<Looper>::make(false); mReporter = createInputReporter(); mWindowInfoListener = sp<DispatcherWindowListener>::make(*this); @@ -971,11 +986,6 @@ InputDispatcher::~InputDispatcher() { releasePendingEventLocked(); drainInboundQueueLocked(); mCommandQueue.clear(); - - while (!mConnectionsByToken.empty()) { - std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second; - removeInputChannelLocked(connection, /*notify=*/false); - } } status_t InputDispatcher::start() { @@ -1092,9 +1102,13 @@ nsecs_t InputDispatcher::processAnrsLocked() { } // If we reached here, we have an unresponsive connection. - std::shared_ptr<Connection> connection = getConnectionLocked(mAnrTracker.firstToken()); + std::shared_ptr<Connection> connection = + mConnectionManager.getConnection(mAnrTracker.firstToken()); if (connection == nullptr) { ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout()); + // As we no longer have entry for this connection, remove it form Anr tracker to prevent + // samme error being logged multiple times. + mAnrTracker.eraseToken(mAnrTracker.firstToken()); return nextAnrCheck; } connection->responsive = false; @@ -1344,7 +1358,7 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt motionEntry.deviceId, mTouchStatesByDisplay); for (const auto& windowHandle : touchedSpies) { const std::shared_ptr<Connection> connection = - getConnectionLocked(windowHandle->getToken()); + mConnectionManager.getConnection(windowHandle->getToken()); if (connection != nullptr && connection->responsive) { // This spy window could take more input. Drop all events preceding this // event, so that the spy window can get a chance to receive the stream. @@ -1727,7 +1741,8 @@ void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bo void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry) { - std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken); + std::shared_ptr<Connection> connection = + mConnectionManager.getConnection(entry->connectionToken); if (connection == nullptr) { return; // Connection has gone away } @@ -1795,7 +1810,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( } } - auto connection = getConnectionLocked(token); + auto connection = mConnectionManager.getConnection(token); if (connection == nullptr) { // Window has gone away, clean up Pointer Capture state. mWindowTokenWithPointerCapture = nullptr; @@ -1834,7 +1849,7 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked if (token == nullptr) { continue; } - std::shared_ptr<Connection> connection = getConnectionLocked(token); + std::shared_ptr<Connection> connection = mConnectionManager.getConnection(token); if (connection == nullptr) { continue; // Connection has gone away } @@ -2127,7 +2142,8 @@ void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowH void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry) { - std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken); + std::shared_ptr<Connection> connection = + mConnectionManager.getConnection(entry->connectionToken); if (connection == nullptr) { return; // Connection has gone away } @@ -2371,26 +2387,6 @@ InputDispatcher::findFocusedWindowTargetLocked(nsecs_t currentTime, const EventE return focusedWindowHandle; } -/** - * Given a list of monitors, remove the ones we cannot find a connection for, and the ones - * that are currently unresponsive. - */ -std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( - const std::vector<Monitor>& monitors) const { - std::vector<Monitor> responsiveMonitors; - std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors), - [](const Monitor& monitor) REQUIRES(mLock) { - std::shared_ptr<Connection> connection = monitor.connection; - if (!connection->responsive) { - ALOGW("Unresponsive monitor %s will not get the new gesture", - connection->getInputChannelName().c_str()); - return false; - } - return true; - }); - return responsiveMonitors; -} - base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult> InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) { ATRACE_CALL(); @@ -2412,7 +2408,7 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio tempTouchState = *oldState; } - bool isSplit = shouldSplitTouch(entry.source); + const bool isSplit = shouldSplitTouch(entry.source); const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || @@ -2425,11 +2421,6 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE; - const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); - - if (newGesture) { - isSplit = false; - } if (isDown && tempTouchState.hasHoveringPointers(entry.deviceId)) { // Compatibility behaviour: ACTION_DOWN causes HOVER_EXIT to get generated. @@ -2472,8 +2463,6 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio return injectionError(InputEventInjectionResult::TARGET_MISMATCH); } - isSplit = !isFromMouse; - std::vector<sp<WindowInfoHandle>> newTouchedWindows = mWindowInfos.findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, mTouchStatesByDisplay); @@ -2510,9 +2499,9 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio if (isSplit) { targetFlags |= InputTarget::Flags::SPLIT; } - if (isWindowObscuredAtPointLocked(windowHandle, x, y)) { + if (mWindowInfos.isWindowObscuredAtPoint(windowHandle, x, y)) { targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED; - } else if (isWindowObscuredLocked(windowHandle)) { + } else if (mWindowInfos.isWindowObscured(windowHandle)) { targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; } @@ -2543,7 +2532,8 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio if (isDownOrPointerDown && targetFlags.test(InputTarget::Flags::FOREGROUND) && windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { - sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle); + sp<WindowInfoHandle> wallpaper = + mWindowInfos.findWallpaperWindowBelow(windowHandle); if (wallpaper != nullptr) { ftl::Flags<InputTarget::Flags> wallpaperFlags = InputTarget::Flags::WINDOW_IS_OBSCURED | @@ -2647,7 +2637,6 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio targets); // Make a slippery entrance into the new window. - isSplit = !isFromMouse; ftl::Flags<InputTarget::Flags> targetFlags; if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) { @@ -2656,9 +2645,9 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio if (isSplit) { targetFlags |= InputTarget::Flags::SPLIT; } - if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { + if (mWindowInfos.isWindowObscuredAtPoint(newTouchedWindowHandle, x, y)) { targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED; - } else if (isWindowObscuredLocked(newTouchedWindowHandle)) { + } else if (mWindowInfos.isWindowObscured(newTouchedWindowHandle)) { targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; } @@ -2941,7 +2930,8 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa const WindowInfo* windowInfo = windowHandle->getInfo(); if (it == inputTargets.end()) { - std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken()); + std::shared_ptr<Connection> connection = + mConnectionManager.getConnection(windowHandle->getToken()); if (connection == nullptr) { ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str()); @@ -2995,7 +2985,8 @@ void InputDispatcher::addPointerWindowTargetLocked( const WindowInfo* windowInfo = windowHandle->getInfo(); if (it == inputTargets.end()) { - std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken()); + std::shared_ptr<Connection> connection = + mConnectionManager.getConnection(windowHandle->getToken()); if (connection == nullptr) { ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str()); @@ -3030,18 +3021,30 @@ void InputDispatcher::addPointerWindowTargetLocked( void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, ui::LogicalDisplayId displayId) { - auto monitorsIt = mGlobalMonitorsByDisplay.find(displayId); - if (monitorsIt == mGlobalMonitorsByDisplay.end()) return; - - for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) { - InputTarget target{monitor.connection}; - // 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. - // Since global monitors don't have windows, use the display transform as the raw transform. - target.rawTransform = mWindowInfos.getDisplayTransform(displayId); - target.setDefaultPointerTransform(target.rawTransform); - inputTargets.push_back(target); - } + mConnectionManager + .forEachGlobalMonitorConnection(displayId, + [&](const std::shared_ptr<Connection>& connection) { + if (!connection->responsive) { + ALOGW("Ignoring unrsponsive monitor: %s", + connection->getInputChannelName() + .c_str()); + return; + } + + InputTarget target{connection}; + // 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. Since + // global monitors don't have windows, use the + // display transform as the raw transform. + base::ScopedLockAssertion assumeLocked(mLock); + target.rawTransform = + mWindowInfos.getDisplayTransform(displayId); + target.setDefaultPointerTransform( + target.rawTransform); + inputTargets.push_back(target); + }); } /** @@ -3097,12 +3100,12 @@ static bool canBeObscuredBy(const sp<WindowInfoHandle>& windowHandle, * * If neither of those is true, then it means the touch can be allowed. */ -InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked( +InputDispatcher::DispatcherWindowInfo::TouchOcclusionInfo +InputDispatcher::DispatcherWindowInfo::computeTouchOcclusionInfo( const sp<WindowInfoHandle>& windowHandle, float x, float y) const { const WindowInfo* windowInfo = windowHandle->getInfo(); ui::LogicalDisplayId displayId = windowInfo->displayId; - const std::vector<sp<WindowInfoHandle>>& windowHandles = - mWindowInfos.getWindowHandlesForDisplay(displayId); + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId); TouchOcclusionInfo info; info.hasBlockingOcclusion = false; info.obscuringOpacity = 0; @@ -3114,12 +3117,11 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo } const WindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && - windowOccludesTouchAt(*otherInfo, displayId, x, y, - mWindowInfos.getDisplayTransform(displayId)) && + windowOccludesTouchAt(*otherInfo, displayId, x, y, getDisplayTransform(displayId)) && !haveSameApplicationToken(windowInfo, otherInfo)) { if (DEBUG_TOUCH_OCCLUSION) { info.debugInfo.push_back( - dumpWindowForTouchOcclusion(otherInfo, /*isTouchedWindow=*/false)); + dumpWindowForTouchOcclusion(*otherInfo, /*isTouchedWindow=*/false)); } // canBeObscuredBy() has returned true above, which means this window is untrusted, so // we perform the checks below to see if the touch can be propagated or not based on the @@ -3147,28 +3149,14 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo } } if (DEBUG_TOUCH_OCCLUSION) { - info.debugInfo.push_back(dumpWindowForTouchOcclusion(windowInfo, /*isTouchedWindow=*/true)); + info.debugInfo.push_back( + dumpWindowForTouchOcclusion(*windowInfo, /*isTouchedWindow=*/true)); } return info; } -std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info, - bool isTouchedWindow) const { - return StringPrintf(INDENT2 "* %spackage=%s/%s, id=%" PRId32 ", mode=%s, alpha=%.2f, " - "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32 - "], touchableRegion=%s, window={%s}, inputConfig={%s}, " - "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n", - isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(), - info->ownerUid.toString().c_str(), info->id, - toString(info->touchOcclusionMode).c_str(), info->alpha, info->frame.left, - info->frame.top, info->frame.right, info->frame.bottom, - dumpRegion(info->touchableRegion).c_str(), info->name.c_str(), - info->inputConfig.string().c_str(), toString(info->token != nullptr), - info->applicationInfo.name.c_str(), - binderToString(info->applicationInfo.token).c_str()); -} - -bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const { +bool InputDispatcher::DispatcherWindowInfo::isTouchTrusted( + const TouchOcclusionInfo& occlusionInfo) const { if (occlusionInfo.hasBlockingOcclusion) { ALOGW("Untrusted touch due to occlusion by %s/%s", occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid.toString().c_str()); @@ -3184,29 +3172,27 @@ bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionIn return true; } -bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle, - float x, float y) const { +bool InputDispatcher::DispatcherWindowInfo::isWindowObscuredAtPoint( + const sp<WindowInfoHandle>& windowHandle, float x, float y) const { ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId; - const std::vector<sp<WindowInfoHandle>>& windowHandles = - mWindowInfos.getWindowHandlesForDisplay(displayId); + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId); for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { if (windowHandle == otherHandle) { break; // All future windows are below us. Exit early. } const WindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && - windowOccludesTouchAt(*otherInfo, displayId, x, y, - mWindowInfos.getDisplayTransform(displayId))) { + windowOccludesTouchAt(*otherInfo, displayId, x, y, getDisplayTransform(displayId))) { return true; } } return false; } -bool InputDispatcher::isWindowObscuredLocked(const sp<WindowInfoHandle>& windowHandle) const { +bool InputDispatcher::DispatcherWindowInfo::isWindowObscured( + const sp<WindowInfoHandle>& windowHandle) const { ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId; - const std::vector<sp<WindowInfoHandle>>& windowHandles = - mWindowInfos.getWindowHandlesForDisplay(displayId); + const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId); const WindowInfo* windowInfo = windowHandle->getInfo(); for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { if (windowHandle == otherHandle) { @@ -4000,7 +3986,7 @@ void InputDispatcher::releaseDispatchEntry(std::unique_ptr<DispatchEntry> dispat int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) { std::scoped_lock _l(mLock); - std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken); + std::shared_ptr<Connection> connection = mConnectionManager.getConnection(connectionToken); if (connection == nullptr) { ALOGW("Received looper callback for unknown input channel token %p. events=0x%x", connectionToken.get(), events); @@ -4108,12 +4094,12 @@ void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { - for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { - for (const Monitor& monitor : monitors) { - synthesizeCancelationEventsForConnectionLocked(monitor.connection, options, - /*window=*/nullptr); - } - } + mConnectionManager.forEachGlobalMonitorConnection( + [&](const std::shared_ptr<Connection>& connection) { + base::ScopedLockAssertion assumeLocked(mLock); + synthesizeCancelationEventsForConnectionLocked(connection, options, + /*window=*/nullptr); + }); } void InputDispatcher::synthesizeCancelationEventsForWindowLocked( @@ -4131,7 +4117,7 @@ void InputDispatcher::synthesizeCancelationEventsForWindowLocked( } std::shared_ptr<Connection> resolvedConnection = - connection ? connection : getConnectionLocked(windowHandle->getToken()); + connection ? connection : mConnectionManager.getConnection(windowHandle->getToken()); if (!resolvedConnection) { LOG(DEBUG) << __func__ << "No connection found for window: " << windowHandle->getName(); return; @@ -5280,8 +5266,9 @@ std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() co return dump; } -bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window, - const MotionEntry& motionEntry) const { +bool InputDispatcher::canWindowReceiveMotionLocked( + const sp<android::gui::WindowInfoHandle>& window, + const android::inputdispatcher::MotionEntry& motionEntry) const { const WindowInfo& info = *window->getInfo(); // Skip spy window targets that are not valid for targeted injection. @@ -5300,7 +5287,7 @@ bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& w return false; } - std::shared_ptr<Connection> connection = getConnectionLocked(window->getToken()); + std::shared_ptr<Connection> connection = mConnectionManager.getConnection(window->getToken()); if (connection == nullptr) { ALOGW("Not sending touch to %s because there's no corresponding connection", window->getName().c_str()); @@ -5314,8 +5301,9 @@ bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& w // Drop events that can't be trusted due to occlusion const auto [x, y] = resolveTouchedPosition(motionEntry); - TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(window, x, y); - if (!isTouchTrustedLocked(occlusionInfo)) { + DispatcherWindowInfo::TouchOcclusionInfo occlusionInfo = + mWindowInfos.computeTouchOcclusionInfo(window, x, y); + if (!mWindowInfos.isTouchTrusted(occlusionInfo)) { if (DEBUG_TOUCH_OCCLUSION) { ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y); for (const auto& log : occlusionInfo.debugInfo) { @@ -5363,7 +5351,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( std::vector<sp<WindowInfoHandle>> newHandles; for (const sp<WindowInfoHandle>& handle : windowInfoHandles) { const WindowInfo* info = handle->getInfo(); - if (getConnectionLocked(handle->getToken()) == nullptr) { + if (mConnectionManager.getConnection(handle->getToken()) == nullptr) { const bool noInputChannel = info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); const bool canReceiveInput = @@ -5759,13 +5747,8 @@ bool InputDispatcher::recentWindowsAreOwnedByLocked(gui::Pid pid, gui::Uid uid) } void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { - if (opacity < 0 || opacity > 1) { - LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1"); - return; - } - std::scoped_lock lock(mLock); - mMaximumObscuringOpacityForTouch = opacity; + mWindowInfos.setMaximumObscuringOpacityForTouch(opacity); } std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId> @@ -5869,8 +5852,8 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s // Synthesize cancel for old window and down for new window. ScopedSyntheticEventTracer traceContext(mTracer); - std::shared_ptr<Connection> fromConnection = getConnectionLocked(fromToken); - std::shared_ptr<Connection> toConnection = getConnectionLocked(toToken); + std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken); + std::shared_ptr<Connection> toConnection = mConnectionManager.getConnection(toToken); if (fromConnection != nullptr && toConnection != nullptr) { fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, @@ -6045,18 +6028,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += addLinePrefix(mWindowInfos.dumpDisplayAndWindowInfo(), INDENT); - if (!mGlobalMonitorsByDisplay.empty()) { - for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) { - dump += StringPrintf(INDENT "Global monitors on display %s:\n", - displayId.toString().c_str()); - dumpMonitors(dump, monitors); - } - } else { - dump += INDENT "Global Monitors: <none>\n"; - } - const nsecs_t currentTime = now(); + dump += addLinePrefix(mConnectionManager.dump(currentTime), INDENT); + // Dump recently dispatched or dropped events from oldest to newest. if (!mRecentQueue.empty()) { dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size()); @@ -6098,37 +6073,6 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += INDENT "CommandQueue: <empty>\n"; } - if (!mConnectionsByToken.empty()) { - dump += INDENT "Connections:\n"; - for (const auto& [token, connection] : mConnectionsByToken) { - dump += StringPrintf(INDENT2 "%i: channelName='%s', " - "status=%s, monitor=%s, responsive=%s\n", - connection->inputPublisher.getChannel().getFd(), - connection->getInputChannelName().c_str(), - ftl::enum_string(connection->status).c_str(), - toString(connection->monitor), toString(connection->responsive)); - - if (!connection->outboundQueue.empty()) { - dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", - connection->outboundQueue.size()); - dump += dumpQueue(connection->outboundQueue, currentTime); - } - - if (!connection->waitQueue.empty()) { - dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n", - connection->waitQueue.size()); - dump += dumpQueue(connection->waitQueue, currentTime); - } - std::string inputStateDump = streamableToString(connection->inputState); - if (!inputStateDump.empty()) { - dump += INDENT3 "InputState: "; - dump += inputStateDump + "\n"; - } - } - } else { - dump += INDENT "Connections: <none>\n"; - } - if (!mTouchModePerDisplay.empty()) { dump += INDENT "TouchModePerDisplay:\n"; for (const auto& [displayId, touchMode] : mTouchModePerDisplay) { @@ -6149,16 +6093,6 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += mTracer == nullptr ? "Disabled" : "Enabled"; } -void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const { - const size_t numMonitors = monitors.size(); - for (size_t i = 0; i < numMonitors; i++) { - const Monitor& monitor = monitors[i]; - const std::shared_ptr<Connection>& connection = monitor.connection; - dump += StringPrintf(INDENT2 "%zu: '%s', ", i, connection->getInputChannelName().c_str()); - dump += "\n"; - } -} - class LooperEventCallback : public LooperCallback { public: LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {} @@ -6184,21 +6118,10 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const { // acquire lock std::scoped_lock _l(mLock); const sp<IBinder>& token = serverChannel->getConnectionToken(); - const int fd = serverChannel->getFd(); - std::shared_ptr<Connection> connection = - std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false, - mIdGenerator); - - auto [_, inserted] = mConnectionsByToken.try_emplace(token, connection); - if (!inserted) { - ALOGE("Created a new connection, but the token %p is already known", token.get()); - } - std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), - nullptr); + mConnectionManager.createConnection(std::move(serverChannel), mIdGenerator, callback); } // release lock // Wake the looper because some connections have changed. @@ -6224,23 +6147,11 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor( } const sp<IBinder>& token = serverChannel->getConnectionToken(); - const int fd = serverChannel->getFd(); - std::shared_ptr<Connection> connection = - std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/true, - mIdGenerator); - - auto [_, inserted] = mConnectionsByToken.emplace(token, connection); - if (!inserted) { - ALOGE("Created a new connection, but the token %p is already known", token.get()); - } - std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mGlobalMonitorsByDisplay[displayId].emplace_back(connection, pid); - - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), - nullptr); + mConnectionManager.createGlobalInputMonitor(displayId, std::move(serverChannel), + mIdGenerator, pid, callback); } // Wake the looper because some connections have changed. @@ -6251,7 +6162,7 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor( status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) { { // acquire lock std::scoped_lock _l(mLock); - std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken); + std::shared_ptr<Connection> connection = mConnectionManager.getConnection(connectionToken); if (connection == nullptr) { // Connection can be removed via socket hang up or an explicit call to // 'removeInputChannel' @@ -6274,19 +6185,14 @@ status_t InputDispatcher::removeInputChannelLocked(const std::shared_ptr<Connect bool notify) { LOG_ALWAYS_FATAL_IF(connection == nullptr); abortBrokenDispatchCycleLocked(connection, notify); - removeConnectionLocked(connection); - if (connection->monitor) { - removeMonitorChannelLocked(connection->getToken()); - } - - mLooper->removeFd(connection->inputPublisher.getChannel().getFd()); + mAnrTracker.eraseToken(connection->getToken()); + mConnectionManager.removeConnection(connection); - connection->status = Connection::Status::ZOMBIE; return OK; } -void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionToken) { +void InputDispatcher::ConnectionManager::removeMonitorChannel(const sp<IBinder>& connectionToken) { for (auto it = mGlobalMonitorsByDisplay.begin(); it != mGlobalMonitorsByDisplay.end();) { auto& [displayId, monitors] = *it; std::erase_if(monitors, [connectionToken](const Monitor& monitor) { @@ -6307,7 +6213,8 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { } status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { - const std::shared_ptr<Connection> requestingConnection = getConnectionLocked(token); + const std::shared_ptr<Connection> requestingConnection = + mConnectionManager.getConnection(token); if (!requestingConnection) { LOG(WARNING) << "Attempted to pilfer pointers from an un-registered channel or invalid token"; @@ -6414,7 +6321,8 @@ void InputDispatcher::setDisplayEligibilityForPointerCapture(ui::LogicalDisplayI } // release lock } -std::optional<gui::Pid> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) { +std::optional<gui::Pid> InputDispatcher::ConnectionManager::findMonitorPidByToken( + const sp<IBinder>& token) const { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { if (monitor.connection->getToken() == token) { @@ -6425,7 +6333,7 @@ std::optional<gui::Pid> InputDispatcher::findMonitorPidByTokenLocked(const sp<IB return std::nullopt; } -std::shared_ptr<Connection> InputDispatcher::getConnectionLocked( +std::shared_ptr<Connection> InputDispatcher::ConnectionManager::getConnection( const sp<IBinder>& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; @@ -6440,19 +6348,6 @@ std::shared_ptr<Connection> InputDispatcher::getConnectionLocked( return nullptr; } -std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const { - std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken); - if (connection == nullptr) { - return "<nullptr>"; - } - return connection->getInputChannelName(); -} - -void InputDispatcher::removeConnectionLocked(const std::shared_ptr<Connection>& connection) { - mAnrTracker.eraseToken(connection->getToken()); - mConnectionsByToken.erase(connection->getToken()); -} - void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, const std::shared_ptr<Connection>& connection, uint32_t seq, bool handled, @@ -6677,7 +6572,7 @@ void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& conn if (connection.monitor) { ALOGW("Monitor %s is unresponsive: %s", connection.getInputChannelName().c_str(), reason.c_str()); - pid = findMonitorPidByTokenLocked(connectionToken); + pid = mConnectionManager.findMonitorPidByToken(connectionToken); } else { // The connection is a window ALOGW("Window %s is unresponsive: %s", connection.getInputChannelName().c_str(), @@ -6697,7 +6592,7 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec const sp<IBinder>& connectionToken = connection.getToken(); std::optional<gui::Pid> pid; if (connection.monitor) { - pid = findMonitorPidByTokenLocked(connectionToken); + pid = mConnectionManager.findMonitorPidByToken(connectionToken); } else { // The connection is a window const sp<WindowInfoHandle> handle = mWindowInfos.findWindowHandle(connectionToken); @@ -7147,7 +7042,7 @@ bool InputDispatcher::shouldDropInput( if (windowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::DROP_INPUT) || (windowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED) && - isWindowObscuredLocked(windowHandle))) { + mWindowInfos.isWindowObscured(windowHandle))) { ALOGW("Dropping %s event targeting %s as requested by the input configuration {%s} on " "display %s.", ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(), @@ -7200,7 +7095,7 @@ void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFl const sp<WindowInfoHandle> oldWallpaper = oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr; const sp<WindowInfoHandle> newWallpaper = - newHasWallpaper ? findWallpaperWindowBelow(newWindowHandle) : nullptr; + newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr; if (oldWallpaper == newWallpaper) { return; } @@ -7237,7 +7132,7 @@ void InputDispatcher::transferWallpaperTouch( const sp<WindowInfoHandle> oldWallpaper = oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr; const sp<WindowInfoHandle> newWallpaper = - newHasWallpaper ? findWallpaperWindowBelow(toWindowHandle) : nullptr; + newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr; if (oldWallpaper == newWallpaper) { return; } @@ -7258,10 +7153,10 @@ void InputDispatcher::transferWallpaperTouch( state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, deviceId, pointers, downTimeInTarget); std::shared_ptr<Connection> wallpaperConnection = - getConnectionLocked(newWallpaper->getToken()); + mConnectionManager.getConnection(newWallpaper->getToken()); if (wallpaperConnection != nullptr) { std::shared_ptr<Connection> toConnection = - getConnectionLocked(toWindowHandle->getToken()); + mConnectionManager.getConnection(toWindowHandle->getToken()); toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState); synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, wallpaperConnection, wallpaperFlags, traceTracker); @@ -7269,10 +7164,10 @@ void InputDispatcher::transferWallpaperTouch( } } -sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow( +sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWallpaperWindowBelow( const sp<WindowInfoHandle>& windowHandle) const { const std::vector<sp<WindowInfoHandle>>& windowHandles = - mWindowInfos.getWindowHandlesForDisplay(windowHandle->getInfo()->displayId); + getWindowHandlesForDisplay(windowHandle->getInfo()->displayId); bool foundWindow = false; for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { if (!foundWindow && otherHandle != windowHandle) { @@ -7325,4 +7220,139 @@ void InputDispatcher::setInputMethodConnectionIsActive(bool isActive) { } } +InputDispatcher::ConnectionManager::ConnectionManager(const sp<android::Looper>& looper) + : mLooper(looper) {} + +// This destructor is required to ensure cleanup of each input connection, so that the fd is +// removed from the looper. +InputDispatcher::ConnectionManager::~ConnectionManager() { + while (!mConnectionsByToken.empty()) { + std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second; + removeConnection(connection); + } +} + +void InputDispatcher::ConnectionManager::forEachGlobalMonitorConnection( + std::function<void(const std::shared_ptr<Connection>&)> f) const { + for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { + for (const Monitor& monitor : monitors) { + f(monitor.connection); + } + } +} + +void InputDispatcher::ConnectionManager::forEachGlobalMonitorConnection( + ui::LogicalDisplayId displayId, + std::function<void(const std::shared_ptr<Connection>&)> f) const { + auto monitorsIt = mGlobalMonitorsByDisplay.find(displayId); + if (monitorsIt == mGlobalMonitorsByDisplay.end()) return; + + for (const Monitor& monitor : monitorsIt->second) { + f(monitor.connection); + } +} + +void InputDispatcher::ConnectionManager::createGlobalInputMonitor( + ui::LogicalDisplayId displayId, std::unique_ptr<InputChannel>&& inputChannel, + const android::IdGenerator& idGenerator, gui::Pid pid, std::function<int(int)> callback) { + const int fd = inputChannel->getFd(); + std::shared_ptr<Connection> connection = + std::make_shared<Connection>(std::move(inputChannel), /*monitor=*/true, idGenerator); + sp<IBinder> token = connection->getToken(); + auto [_, inserted] = mConnectionsByToken.emplace(token, connection); + if (!inserted) { + ALOGE("Created a new connection, but the token %p is already known", token.get()); + } + mGlobalMonitorsByDisplay[displayId].emplace_back(connection, pid); + + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr); +} + +void InputDispatcher::ConnectionManager::createConnection( + std::unique_ptr<InputChannel>&& inputChannel, const android::IdGenerator& idGenerator, + std::function<int(int)> callback) { + const int fd = inputChannel->getFd(); + std::shared_ptr<Connection> connection = + std::make_shared<Connection>(std::move(inputChannel), /*monitor=*/false, idGenerator); + sp<IBinder> token = connection->getToken(); + auto [_, inserted] = mConnectionsByToken.try_emplace(token, connection); + if (!inserted) { + ALOGE("Created a new connection, but the token %p is already known", token.get()); + } + + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr); +} + +status_t InputDispatcher::ConnectionManager::removeConnection( + const std::shared_ptr<Connection>& connection) { + mConnectionsByToken.erase(connection->getToken()); + + if (connection->monitor) { + removeMonitorChannel(connection->getToken()); + } + + mLooper->removeFd(connection->inputPublisher.getChannel().getFd()); + + connection->status = Connection::Status::ZOMBIE; + return OK; +} + +std::string InputDispatcher::ConnectionManager::dump(nsecs_t currentTime) const { + std::string dump; + if (!mGlobalMonitorsByDisplay.empty()) { + for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) { + dump += StringPrintf("Global monitors on display %s:\n", displayId.toString().c_str()); + const size_t numMonitors = monitors.size(); + for (size_t i = 0; i < numMonitors; i++) { + const Monitor& monitor = monitors[i]; + const std::shared_ptr<Connection>& connection = monitor.connection; + dump += StringPrintf(INDENT "%zu: '%s', ", i, + connection->getInputChannelName().c_str()); + dump += "\n"; + } + } + } else { + dump += "Global Monitors: <none>\n"; + } + + if (!mConnectionsByToken.empty()) { + dump += "Connections:\n"; + for (const auto& [token, connection] : mConnectionsByToken) { + dump += StringPrintf(INDENT "%i: channelName='%s', " + "status=%s, monitor=%s, responsive=%s\n", + connection->inputPublisher.getChannel().getFd(), + connection->getInputChannelName().c_str(), + ftl::enum_string(connection->status).c_str(), + toString(connection->monitor), toString(connection->responsive)); + + if (!connection->outboundQueue.empty()) { + dump += StringPrintf(INDENT2 "OutboundQueue: length=%zu\n", + connection->outboundQueue.size()); + dump += dumpQueue(connection->outboundQueue, currentTime); + } + + if (!connection->waitQueue.empty()) { + dump += StringPrintf(INDENT2 "WaitQueue: length=%zu\n", + connection->waitQueue.size()); + dump += dumpQueue(connection->waitQueue, currentTime); + } + std::string inputStateDump = streamableToString(connection->inputState); + if (!inputStateDump.empty()) { + dump += INDENT2 "InputState: "; + dump += inputStateDump + "\n"; + } + } + } else { + dump += "Connections: <none>\n"; + } + return dump; +} + +void InputDispatcher::DispatcherWindowInfo::setMaximumObscuringOpacityForTouch(float opacity) { + if (opacity < 0 || opacity > 1) { + LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1"); + } + mMaximumObscuringOpacityForTouch = opacity; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index bca1c67a43..415f4c8031 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -224,6 +224,132 @@ private: /** Stores the latest user-activity poke event times per user activity types. */ std::array<nsecs_t, USER_ACTIVITY_EVENT_LAST + 1> mLastUserActivityTimes GUARDED_BY(mLock); + template <typename T> + struct StrongPointerHash { + std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); } + }; + + class ConnectionManager { + public: + ConnectionManager(const sp<Looper>& lopper); + ~ConnectionManager(); + + std::shared_ptr<Connection> getConnection(const sp<IBinder>& inputConnectionToken) const; + + // Find a monitor pid by the provided token. + std::optional<gui::Pid> findMonitorPidByToken(const sp<IBinder>& token) const; + void forEachGlobalMonitorConnection( + std::function<void(const std::shared_ptr<Connection>&)> f) const; + void forEachGlobalMonitorConnection( + ui::LogicalDisplayId displayId, + std::function<void(const std::shared_ptr<Connection>&)> f) const; + + void createGlobalInputMonitor(ui::LogicalDisplayId displayId, + std::unique_ptr<InputChannel>&& inputChannel, + const IdGenerator& idGenerator, gui::Pid pid, + std::function<int(int)> callback); + + status_t removeConnection(const std::shared_ptr<Connection>& connection); + + void createConnection(std::unique_ptr<InputChannel>&& inputChannel, + const IdGenerator& idGenerator, std::function<int(int)> callback); + + std::string dump(nsecs_t currentTime) const; + + private: + const sp<Looper> mLooper; + + // All registered connections mapped by input channel token. + std::unordered_map<sp<IBinder>, std::shared_ptr<Connection>, StrongPointerHash<IBinder>> + mConnectionsByToken; + + // Input channels that will receive a copy of all input events sent to the provided display. + std::unordered_map<ui::LogicalDisplayId, std::vector<Monitor>> mGlobalMonitorsByDisplay; + + void removeMonitorChannel(const sp<IBinder>& connectionToken); + }; + + ConnectionManager mConnectionManager GUARDED_BY(mLock); + + class DispatcherWindowInfo { + public: + struct TouchOcclusionInfo { + bool hasBlockingOcclusion; + float obscuringOpacity; + std::string obscuringPackage; + gui::Uid obscuringUid = gui::Uid::INVALID; + std::vector<std::string> debugInfo; + }; + + void setWindowHandlesForDisplay( + ui::LogicalDisplayId displayId, + std::vector<sp<android::gui::WindowInfoHandle>>&& windowHandles); + + void setDisplayInfos(const std::vector<android::gui::DisplayInfo>& displayInfos); + + void removeDisplay(ui::LogicalDisplayId displayId); + + void setMaximumObscuringOpacityForTouch(float opacity); + + // Get a reference to window handles by display, return an empty vector if not found. + const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesForDisplay( + ui::LogicalDisplayId displayId) const; + + void forEachWindowHandle( + std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const; + + void forEachDisplayId(std::function<void(ui::LogicalDisplayId)> f) const; + + // Get the transform for display, returns Identity-transform if display is missing. + ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const; + + // Get the raw transform to use for motion events going to the given window. + ui::Transform getRawTransform(const android::gui::WindowInfo&) const; + + // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where + // display-id is not provided lookup is done for all displays. + sp<android::gui::WindowInfoHandle> findWindowHandle( + const sp<IBinder>& windowHandleToken, + std::optional<ui::LogicalDisplayId> displayId = {}) const; + + bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const; + + // Returns the touched window at the given location, excluding the ignoreWindow if provided. + sp<android::gui::WindowInfoHandle> findTouchedWindowAt( + ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false, + const sp<android::gui::WindowInfoHandle> ignoreWindow = nullptr) const; + + std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAt( + ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId, + const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay) + const; + + TouchOcclusionInfo computeTouchOcclusionInfo( + const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const; + + bool isWindowObscured(const sp<android::gui::WindowInfoHandle>& windowHandle) const; + + bool isWindowObscuredAtPoint(const sp<android::gui::WindowInfoHandle>& windowHandle, + float x, float y) const; + + sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow( + const sp<android::gui::WindowInfoHandle>& windowHandle) const; + + bool isTouchTrusted(const TouchOcclusionInfo& occlusionInfo) const; + + std::string dumpDisplayAndWindowInfo() const; + + private: + std::unordered_map<ui::LogicalDisplayId /*displayId*/, + std::vector<sp<android::gui::WindowInfoHandle>>> + mWindowHandlesByDisplay; + std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo> + mDisplayInfos; + float mMaximumObscuringOpacityForTouch{1.0f}; + }; + + DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock); + // With each iteration, InputDispatcher nominally processes one queued event, // a timeout, or a response from an input consumer. // This method should only be called on the input dispatcher's own thread. @@ -260,31 +386,8 @@ private: const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay, ui::LogicalDisplayId displayId); - std::shared_ptr<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const - REQUIRES(mLock); - - std::string getConnectionNameLocked(const sp<IBinder>& connectionToken) const REQUIRES(mLock); - - void removeConnectionLocked(const std::shared_ptr<Connection>& connection) REQUIRES(mLock); - status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock); - template <typename T> - struct StrongPointerHash { - std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); } - }; - - // All registered connections mapped by input channel token. - std::unordered_map<sp<IBinder>, std::shared_ptr<Connection>, StrongPointerHash<IBinder>> - mConnectionsByToken GUARDED_BY(mLock); - - // Find a monitor pid by the provided token. - std::optional<gui::Pid> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock); - - // Input channels that will receive a copy of all input events sent to the provided display. - std::unordered_map<ui::LogicalDisplayId, std::vector<Monitor>> mGlobalMonitorsByDisplay - GUARDED_BY(mLock); - const HmacKeyManager mHmacKeyManager; const std::array<uint8_t, 32> getSignature(const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const; @@ -344,7 +447,6 @@ private: bool mDispatchEnabled GUARDED_BY(mLock); bool mDispatchFrozen GUARDED_BY(mLock); bool mInputFilterEnabled GUARDED_BY(mLock); - float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock); // This map is not really needed, but it helps a lot with debugging (dumpsys input). // In the java layer, touch mode states are spread across multiple DisplayContent objects, @@ -362,61 +464,6 @@ private: }; sp<gui::WindowInfosListener> mWindowInfoListener; - class DispatcherWindowInfo { - public: - void setWindowHandlesForDisplay( - ui::LogicalDisplayId displayId, - std::vector<sp<android::gui::WindowInfoHandle>>&& windowHandles); - - void setDisplayInfos(const std::vector<android::gui::DisplayInfo>& displayInfos); - - void removeDisplay(ui::LogicalDisplayId displayId); - - // Get a reference to window handles by display, return an empty vector if not found. - const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesForDisplay( - ui::LogicalDisplayId displayId) const; - - void forEachWindowHandle( - std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const; - - void forEachDisplayId(std::function<void(ui::LogicalDisplayId)> f) const; - - // Get the transform for display, returns Identity-transform if display is missing. - ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const; - - // Get the raw transform to use for motion events going to the given window. - ui::Transform getRawTransform(const android::gui::WindowInfo&) const; - - // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where - // display-id is not provided lookup is done for all displays. - sp<android::gui::WindowInfoHandle> findWindowHandle( - const sp<IBinder>& windowHandleToken, - std::optional<ui::LogicalDisplayId> displayId = {}) const; - - bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const; - - // Returns the touched window at the given location, excluding the ignoreWindow if provided. - sp<android::gui::WindowInfoHandle> findTouchedWindowAt( - ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false, - const sp<android::gui::WindowInfoHandle> ignoreWindow = nullptr) const; - - std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAt( - ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId, - const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay) - const; - - std::string dumpDisplayAndWindowInfo() const; - - private: - std::unordered_map<ui::LogicalDisplayId /*displayId*/, - std::vector<sp<android::gui::WindowInfoHandle>>> - mWindowHandlesByDisplay; - std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo> - mDisplayInfos; - }; - - DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock); - void setInputWindowsLocked( const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles, ui::LogicalDisplayId displayId) REQUIRES(mLock); @@ -582,8 +629,6 @@ private: nsecs_t& nextWakeupTime) REQUIRES(mLock); base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult> findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) REQUIRES(mLock); - std::vector<Monitor> selectResponsiveMonitorsLocked( - const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock); void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, @@ -604,24 +649,6 @@ private: void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock); void finishDragAndDrop(ui::LogicalDisplayId displayId, float x, float y) REQUIRES(mLock); - struct TouchOcclusionInfo { - bool hasBlockingOcclusion; - float obscuringOpacity; - std::string obscuringPackage; - gui::Uid obscuringUid = gui::Uid::INVALID; - std::vector<std::string> debugInfo; - }; - - TouchOcclusionInfo computeTouchOcclusionInfoLocked( - const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const - REQUIRES(mLock); - bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock); - bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, - float x, float y) const REQUIRES(mLock); - bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const - REQUIRES(mLock); - std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info, - bool isTouchWindow) const; std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle, const sp<android::gui::WindowInfoHandle>& windowHandle); @@ -689,12 +716,9 @@ private: // Dump state. void dumpDispatchStateLocked(std::string& dump) const REQUIRES(mLock); - void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const; void logDispatchStateLocked() const REQUIRES(mLock); std::string dumpPointerCaptureStateLocked() const REQUIRES(mLock); - // Registration. - void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock); status_t removeInputChannelLocked(const std::shared_ptr<Connection>& connection, bool notify) REQUIRES(mLock); @@ -777,9 +801,6 @@ private: const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) REQUIRES(mLock); - sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow( - const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); - /** Stores the value of the input flag for per device input latency metrics. */ const bool mPerDeviceInputLatencyMetricsFlag = com::android::input::flags::enable_per_device_input_latency_metrics(); diff --git a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp index 881a96b28e..4da05c19cf 100644 --- a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp +++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp @@ -133,10 +133,11 @@ void LatencyAggregatorWithHistograms::addSampleToHistogram( } void LatencyAggregatorWithHistograms::processStatistics(const InputEventTimeline& timeline) { - // Only gather data for Down, Move and Up motion events and Key events + // Only gather data for Down, Move, Up and Scroll motion events and Key events if (!(timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN || timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_MOVE || timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_UP || + timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_SCROLL || timeline.inputEventActionType == InputEventActionType::KEY)) return; diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp deleted file mode 100644 index cc0468464e..0000000000 --- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2024 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 "AndroidInputEventProtoConverter.h" - -#include <android-base/logging.h> -#include <perfetto/trace/android/android_input_event.pbzero.h> - -namespace android::inputdispatcher::trace { - -namespace { - -using namespace ftl::flag_operators; - -// The trace config to use for maximal tracing. -const impl::TraceConfig CONFIG_TRACE_ALL{ - .flags = impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS | - impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH, - .rules = {impl::TraceRule{.level = impl::TraceLevel::TRACE_LEVEL_COMPLETE, - .matchAllPackages = {}, - .matchAnyPackages = {}, - .matchSecure{}, - .matchImeConnectionActive = {}}}, -}; - -} // namespace - -void AndroidInputEventProtoConverter::toProtoMotionEvent(const TracedMotionEvent& event, - proto::AndroidMotionEvent& outProto, - bool isRedacted) { - outProto.set_event_id(event.id); - outProto.set_event_time_nanos(event.eventTime); - outProto.set_down_time_nanos(event.downTime); - outProto.set_source(event.source); - outProto.set_action(event.action); - outProto.set_device_id(event.deviceId); - outProto.set_display_id(event.displayId.val()); - outProto.set_classification(static_cast<int32_t>(event.classification)); - outProto.set_flags(event.flags); - outProto.set_policy_flags(event.policyFlags); - - if (!isRedacted) { - outProto.set_cursor_position_x(event.xCursorPosition); - outProto.set_cursor_position_y(event.yCursorPosition); - outProto.set_meta_state(event.metaState); - } - - for (uint32_t i = 0; i < event.pointerProperties.size(); i++) { - auto* pointer = outProto.add_pointer(); - - const auto& props = event.pointerProperties[i]; - pointer->set_pointer_id(props.id); - pointer->set_tool_type(static_cast<int32_t>(props.toolType)); - - const auto& coords = event.pointerCoords[i]; - auto bits = BitSet64(coords.bits); - for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { - const auto axis = bits.clearFirstMarkedBit(); - auto axisEntry = pointer->add_axis_value(); - axisEntry->set_axis(axis); - - if (!isRedacted) { - axisEntry->set_value(coords.values[axisIndex]); - } - } - } -} - -void AndroidInputEventProtoConverter::toProtoKeyEvent(const TracedKeyEvent& event, - proto::AndroidKeyEvent& outProto, - bool isRedacted) { - outProto.set_event_id(event.id); - outProto.set_event_time_nanos(event.eventTime); - outProto.set_down_time_nanos(event.downTime); - outProto.set_source(event.source); - outProto.set_action(event.action); - outProto.set_device_id(event.deviceId); - outProto.set_display_id(event.displayId.val()); - outProto.set_repeat_count(event.repeatCount); - outProto.set_flags(event.flags); - outProto.set_policy_flags(event.policyFlags); - - if (!isRedacted) { - outProto.set_key_code(event.keyCode); - outProto.set_scan_code(event.scanCode); - outProto.set_meta_state(event.metaState); - } -} - -void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent( - const WindowDispatchArgs& args, proto::AndroidWindowInputDispatchEvent& outProto, - bool isRedacted) { - std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry); - outProto.set_vsync_id(args.vsyncId); - outProto.set_window_id(args.windowId); - outProto.set_resolved_flags(args.resolvedFlags); - - if (isRedacted) { - return; - } - if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) { - for (size_t i = 0; i < motion->pointerProperties.size(); i++) { - auto* pointerProto = outProto.add_dispatched_pointer(); - pointerProto->set_pointer_id(motion->pointerProperties[i].id); - const auto& coords = motion->pointerCoords[i]; - const auto rawXY = - MotionEvent::calculateTransformedXY(motion->source, args.rawTransform, - coords.getXYValue()); - if (coords.getXYValue() != rawXY) { - // These values are only traced if they were modified by the raw transform - // to save space. Trace consumers should be aware of this optimization. - pointerProto->set_x_in_display(rawXY.x); - pointerProto->set_y_in_display(rawXY.y); - } - - const auto coordsInWindow = - MotionEvent::calculateTransformedCoords(motion->source, motion->flags, - args.transform, coords); - auto bits = BitSet64(coords.bits); - for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { - const uint32_t axis = bits.clearFirstMarkedBit(); - const float axisValueInWindow = coordsInWindow.values[axisIndex]; - // Only values that are modified by the window transform are traced. - if (coords.values[axisIndex] != axisValueInWindow) { - auto* axisEntry = pointerProto->add_axis_value_in_window(); - axisEntry->set_axis(axis); - axisEntry->set_value(axisValueInWindow); - } - } - } - } -} - -impl::TraceConfig AndroidInputEventProtoConverter::parseConfig( - proto::AndroidInputEventConfig::Decoder& protoConfig) { - if (protoConfig.has_mode() && - protoConfig.mode() == proto::AndroidInputEventConfig::TRACE_MODE_TRACE_ALL) { - // User has requested the preset for maximal tracing - return CONFIG_TRACE_ALL; - } - - impl::TraceConfig config; - - // Parse trace flags - if (protoConfig.has_trace_dispatcher_input_events() && - protoConfig.trace_dispatcher_input_events()) { - config.flags |= impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS; - } - if (protoConfig.has_trace_dispatcher_window_dispatch() && - protoConfig.trace_dispatcher_window_dispatch()) { - config.flags |= impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH; - } - - // Parse trace rules - auto rulesIt = protoConfig.rules(); - while (rulesIt) { - proto::AndroidInputEventConfig::TraceRule::Decoder protoRule{rulesIt->as_bytes()}; - config.rules.emplace_back(); - auto& rule = config.rules.back(); - - rule.level = protoRule.has_trace_level() - ? static_cast<impl::TraceLevel>(protoRule.trace_level()) - : impl::TraceLevel::TRACE_LEVEL_NONE; - - if (protoRule.has_match_all_packages()) { - auto pkgIt = protoRule.match_all_packages(); - while (pkgIt) { - rule.matchAllPackages.emplace_back(pkgIt->as_std_string()); - pkgIt++; - } - } - - if (protoRule.has_match_any_packages()) { - auto pkgIt = protoRule.match_any_packages(); - while (pkgIt) { - rule.matchAnyPackages.emplace_back(pkgIt->as_std_string()); - pkgIt++; - } - } - - if (protoRule.has_match_secure()) { - rule.matchSecure = protoRule.match_secure(); - } - - if (protoRule.has_match_ime_connection_active()) { - rule.matchImeConnectionActive = protoRule.match_ime_connection_active(); - } - - rulesIt++; - } - - return config; -} - -} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h index 887913f463..c19d278370 100644 --- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h @@ -26,20 +26,214 @@ namespace proto = perfetto::protos::pbzero; namespace android::inputdispatcher::trace { +namespace internal { + +using namespace ftl::flag_operators; + +// The trace config to use for maximal tracing. +const impl::TraceConfig CONFIG_TRACE_ALL{ + .flags = impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS | + impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH, + .rules = {impl::TraceRule{.level = impl::TraceLevel::TRACE_LEVEL_COMPLETE, + .matchAllPackages = {}, + .matchAnyPackages = {}, + .matchSecure{}, + .matchImeConnectionActive = {}}}, +}; + +template <typename Pointer> +void writeAxisValue(Pointer* pointer, int32_t axis, float value, bool isRedacted) { + auto* axisEntry = pointer->add_axis_value(); + axisEntry->set_axis(axis); + + if (!isRedacted) { + axisEntry->set_value(value); + } +} + +} // namespace internal + /** * Write traced events into Perfetto protos. + * + * This class is templated so that the logic can be tested while substituting the proto classes + * auto-generated by Perfetto's pbzero library with mock implementations. */ +template <typename ProtoMotion, typename ProtoKey, typename ProtoDispatch, + typename ProtoConfigDecoder> class AndroidInputEventProtoConverter { public: - static void toProtoMotionEvent(const TracedMotionEvent& event, - proto::AndroidMotionEvent& outProto, bool isRedacted); - static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto, - bool isRedacted); - static void toProtoWindowDispatchEvent(const WindowDispatchArgs&, - proto::AndroidWindowInputDispatchEvent& outProto, - bool isRedacted); - - static impl::TraceConfig parseConfig(proto::AndroidInputEventConfig::Decoder& protoConfig); + static void toProtoMotionEvent(const TracedMotionEvent& event, ProtoMotion& outProto, + bool isRedacted) { + outProto.set_event_id(event.id); + outProto.set_event_time_nanos(event.eventTime); + outProto.set_down_time_nanos(event.downTime); + outProto.set_source(event.source); + outProto.set_action(event.action); + outProto.set_device_id(event.deviceId); + outProto.set_display_id(event.displayId.val()); + outProto.set_classification(static_cast<int32_t>(event.classification)); + outProto.set_flags(event.flags); + outProto.set_policy_flags(event.policyFlags); + outProto.set_button_state(event.buttonState); + outProto.set_action_button(event.actionButton); + + if (!isRedacted) { + outProto.set_cursor_position_x(event.xCursorPosition); + outProto.set_cursor_position_y(event.yCursorPosition); + outProto.set_meta_state(event.metaState); + outProto.set_precision_x(event.xPrecision); + outProto.set_precision_y(event.yPrecision); + } + + for (uint32_t i = 0; i < event.pointerProperties.size(); i++) { + auto* pointer = outProto.add_pointer(); + + const auto& props = event.pointerProperties[i]; + pointer->set_pointer_id(props.id); + pointer->set_tool_type(static_cast<int32_t>(props.toolType)); + + const auto& coords = event.pointerCoords[i]; + auto bits = BitSet64(coords.bits); + + if (isFromSource(event.source, AINPUT_SOURCE_CLASS_POINTER)) { + // Always include the X and Y axes for pointer events, since the + // bits will not be marked if the value is 0. + for (const auto axis : {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y}) { + if (!bits.hasBit(axis)) { + internal::writeAxisValue(pointer, axis, 0.0f, isRedacted); + } + } + } + + for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { + const auto axis = bits.clearFirstMarkedBit(); + internal::writeAxisValue(pointer, axis, coords.values[axisIndex], isRedacted); + } + } + } + + static void toProtoKeyEvent(const TracedKeyEvent& event, ProtoKey& outProto, bool isRedacted) { + outProto.set_event_id(event.id); + outProto.set_event_time_nanos(event.eventTime); + outProto.set_down_time_nanos(event.downTime); + outProto.set_source(event.source); + outProto.set_action(event.action); + outProto.set_device_id(event.deviceId); + outProto.set_display_id(event.displayId.val()); + outProto.set_repeat_count(event.repeatCount); + outProto.set_flags(event.flags); + outProto.set_policy_flags(event.policyFlags); + + if (!isRedacted) { + outProto.set_key_code(event.keyCode); + outProto.set_scan_code(event.scanCode); + outProto.set_meta_state(event.metaState); + } + } + + static void toProtoWindowDispatchEvent(const WindowDispatchArgs& args, ProtoDispatch& outProto, + bool isRedacted) { + std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry); + outProto.set_vsync_id(args.vsyncId); + outProto.set_window_id(args.windowId); + outProto.set_resolved_flags(args.resolvedFlags); + + if (isRedacted) { + return; + } + if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) { + for (size_t i = 0; i < motion->pointerProperties.size(); i++) { + auto* pointerProto = outProto.add_dispatched_pointer(); + pointerProto->set_pointer_id(motion->pointerProperties[i].id); + const auto& coords = motion->pointerCoords[i]; + const auto rawXY = + MotionEvent::calculateTransformedXY(motion->source, args.rawTransform, + coords.getXYValue()); + if (coords.getXYValue() != rawXY) { + // These values are only traced if they were modified by the raw transform + // to save space. Trace consumers should be aware of this optimization. + pointerProto->set_x_in_display(rawXY.x); + pointerProto->set_y_in_display(rawXY.y); + } + + const auto coordsInWindow = + MotionEvent::calculateTransformedCoords(motion->source, motion->flags, + args.transform, coords); + auto bits = BitSet64(coords.bits); + for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { + const uint32_t axis = bits.clearFirstMarkedBit(); + const float axisValueInWindow = coordsInWindow.values[axisIndex]; + // Only values that are modified by the window transform are traced. + if (coords.values[axisIndex] != axisValueInWindow) { + auto* axisEntry = pointerProto->add_axis_value_in_window(); + axisEntry->set_axis(axis); + axisEntry->set_value(axisValueInWindow); + } + } + } + } + } + + static impl::TraceConfig parseConfig(ProtoConfigDecoder& protoConfig) { + if (protoConfig.has_mode() && + protoConfig.mode() == proto::AndroidInputEventConfig::TRACE_MODE_TRACE_ALL) { + // User has requested the preset for maximal tracing + return internal::CONFIG_TRACE_ALL; + } + + impl::TraceConfig config; + + // Parse trace flags + if (protoConfig.has_trace_dispatcher_input_events() && + protoConfig.trace_dispatcher_input_events()) { + config.flags |= impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS; + } + if (protoConfig.has_trace_dispatcher_window_dispatch() && + protoConfig.trace_dispatcher_window_dispatch()) { + config.flags |= impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH; + } + + // Parse trace rules + auto rulesIt = protoConfig.rules(); + while (rulesIt) { + proto::AndroidInputEventConfig::TraceRule::Decoder protoRule{rulesIt->as_bytes()}; + config.rules.emplace_back(); + auto& rule = config.rules.back(); + + rule.level = protoRule.has_trace_level() + ? static_cast<impl::TraceLevel>(protoRule.trace_level()) + : impl::TraceLevel::TRACE_LEVEL_NONE; + + if (protoRule.has_match_all_packages()) { + auto pkgIt = protoRule.match_all_packages(); + while (pkgIt) { + rule.matchAllPackages.emplace_back(pkgIt->as_std_string()); + pkgIt++; + } + } + + if (protoRule.has_match_any_packages()) { + auto pkgIt = protoRule.match_any_packages(); + while (pkgIt) { + rule.matchAnyPackages.emplace_back(pkgIt->as_std_string()); + pkgIt++; + } + } + + if (protoRule.has_match_secure()) { + rule.matchSecure = protoRule.match_secure(); + } + + if (protoRule.has_match_ime_connection_active()) { + rule.matchImeConnectionActive = protoRule.match_ime_connection_active(); + } + + rulesIt++; + } + + return config; + } }; } // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h index 761d619cec..2ff6e1c84d 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h +++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h @@ -50,7 +50,7 @@ struct TracedKeyEvent { uint32_t policyFlags; int32_t deviceId; uint32_t source; - ui::LogicalDisplayId displayId; + ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID; int32_t action; int32_t keyCode; int32_t scanCode; @@ -70,7 +70,7 @@ struct TracedMotionEvent { uint32_t policyFlags; int32_t deviceId; uint32_t source; - ui::LogicalDisplayId displayId; + ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID; int32_t action; int32_t actionButton; int32_t flags; @@ -108,7 +108,7 @@ struct WindowDispatchArgs { TracedEvent eventEntry; nsecs_t deliveryTime; int32_t resolvedFlags; - gui::Uid targetUid; + gui::Uid targetUid = gui::Uid::INVALID; int64_t vsyncId; int32_t windowId; ui::Transform transform; diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp index 77b5c2ebcd..ebcd9c986e 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp @@ -34,6 +34,11 @@ namespace { constexpr auto INPUT_EVENT_TRACE_DATA_SOURCE_NAME = "android.input.inputevent"; +using ProtoConverter = + AndroidInputEventProtoConverter<proto::AndroidMotionEvent, proto::AndroidKeyEvent, + proto::AndroidWindowInputDispatchEvent, + proto::AndroidInputEventConfig::Decoder>; + bool isPermanentlyAllowed(gui::Uid uid) { switch (uid.val()) { case AID_SYSTEM: @@ -85,7 +90,7 @@ void PerfettoBackend::InputEventDataSource::OnSetup(const InputEventDataSource:: const auto rawConfig = args.config->android_input_event_config_raw(); auto protoConfig = perfetto::protos::pbzero::AndroidInputEventConfig::Decoder{rawConfig}; - mConfig = AndroidInputEventProtoConverter::parseConfig(protoConfig); + mConfig = ProtoConverter::parseConfig(protoConfig); } void PerfettoBackend::InputEventDataSource::OnStart(const InputEventDataSource::StartArgs&) { @@ -238,7 +243,7 @@ void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event, auto* inputEvent = winscopeExtensions->set_android_input_event(); auto* dispatchMotion = isRedacted ? inputEvent->set_dispatcher_motion_event_redacted() : inputEvent->set_dispatcher_motion_event(); - AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted); + ProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted); }); } @@ -266,7 +271,7 @@ void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event, auto* inputEvent = winscopeExtensions->set_android_input_event(); auto* dispatchKey = isRedacted ? inputEvent->set_dispatcher_key_event_redacted() : inputEvent->set_dispatcher_key_event(); - AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted); + ProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted); }); } @@ -295,8 +300,7 @@ void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs auto* dispatchEvent = isRedacted ? inputEvent->set_dispatcher_window_dispatch_event_redacted() : inputEvent->set_dispatcher_window_dispatch_event(); - AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, *dispatchEvent, - isRedacted); + ProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, *dispatchEvent, isRedacted); }); } diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index f54b76b3bf..608bec4a0c 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -150,6 +150,11 @@ struct InputReaderConfiguration { // speed setting still affects the scaling factor. bool mousePointerAccelerationEnabled; + // True if the touchpad should exhibit pointer acceleration. If false, + // a flat acceleration curve (linear scaling) is used, but the user's pointer + // speed setting still affects the scaling factor. + bool touchpadAccelerationEnabled; + // Velocity control parameters for touchpad pointer movements on the old touchpad stack (based // on TouchInputMapper). // @@ -284,6 +289,7 @@ struct InputReaderConfiguration { mousePointerSpeed(0), displaysWithMouseScalingDisabled(), mousePointerAccelerationEnabled(true), + touchpadAccelerationEnabled(true), pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, static_cast<float>( android::os::IInputConstants:: diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 24919b678d..207806d891 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -1085,7 +1085,7 @@ EventHubInterface* InputReader::ContextImpl::getEventHub() { return mReader->mEventHub.get(); } -int32_t InputReader::ContextImpl::getNextId() { +int32_t InputReader::ContextImpl::getNextId() const { return mIdGenerator.nextId(); } diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 1403ca2986..931766bc1d 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -154,7 +154,7 @@ protected: REQUIRES(mReader->mLock) override; InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override; EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override; - int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override; + int32_t getNextId() const NO_THREAD_SAFETY_ANALYSIS override; void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override; int32_t getLedMetaState() REQUIRES(mReader->mLock) REQUIRES(mLock) override; void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock) diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index e0e0ac2051..20ed74fef7 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -55,7 +55,7 @@ public: virtual InputReaderPolicyInterface* getPolicy() = 0; virtual EventHubInterface* getEventHub() = 0; - virtual int32_t getNextId() = 0; + virtual int32_t getNextId() const = 0; virtual void updateLedMetaState(int32_t metaState) = 0; virtual int32_t getLedMetaState() = 0; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 9f584a0251..e21c2f9120 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -419,7 +419,7 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod } } -std::optional<ui::LogicalDisplayId> CursorInputMapper::getAssociatedDisplayId() { +std::optional<ui::LogicalDisplayId> CursorInputMapper::getAssociatedDisplayId() const { return mDisplayId; } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 83199227b1..301632ff7a 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -63,7 +63,7 @@ public: virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; - virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override; + virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const override; private: // Amount that trackball needs to move in order to generate a key event. diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index d4a86acd79..630c3d9e93 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -66,11 +66,12 @@ public: virtual ~InputMapper(); - inline int32_t getDeviceId() { return mDeviceContext.getId(); } + inline int32_t getDeviceId() const { return mDeviceContext.getId(); } inline InputDeviceContext& getDeviceContext() { return mDeviceContext; } inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; }; inline const std::string getDeviceName() const { return mDeviceContext.getName(); } inline InputReaderContext* getContext() { return mDeviceContext.getContext(); } + inline const InputReaderContext* getContext() const { return mDeviceContext.getContext(); } inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); } virtual uint32_t getSources() const = 0; @@ -114,7 +115,9 @@ public: [[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state); - virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() { return std::nullopt; } + virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const { + return std::nullopt; + } virtual void updateLedState(bool reset) {} virtual std::optional<HardwareProperties> getTouchpadHardwareProperties(); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index fe3e4c29e5..400792bc60 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -104,14 +104,14 @@ uint32_t KeyboardInputMapper::getSources() const { return mMapperSource; } -ui::Rotation KeyboardInputMapper::getOrientation() { +ui::Rotation KeyboardInputMapper::getOrientation() const { if (mViewport) { return mViewport->orientation; } return ui::ROTATION_0; } -ui::LogicalDisplayId KeyboardInputMapper::getDisplayId() { +ui::LogicalDisplayId KeyboardInputMapper::getDisplayId() const { if (mViewport) { return mViewport->displayId; } @@ -471,7 +471,7 @@ void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t } } -std::optional<ui::LogicalDisplayId> KeyboardInputMapper::getAssociatedDisplayId() { +std::optional<ui::LogicalDisplayId> KeyboardInputMapper::getAssociatedDisplayId() const { if (mViewport) { return std::make_optional(mViewport->displayId); } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 7d9b3e44fa..9e2a81b16d 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -47,7 +47,7 @@ public: int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override; int32_t getMetaState() override; - std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override; + std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const override; void updateLedState(bool reset) override; private: @@ -96,8 +96,8 @@ private: void configureParameters(); void dumpParameters(std::string& dump) const; - ui::Rotation getOrientation(); - ui::LogicalDisplayId getDisplayId(); + ui::Rotation getOrientation() const; + ui::LogicalDisplayId getDisplayId() const; [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 6efaecaaa8..5cfda037d9 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -3666,7 +3666,7 @@ NotifyMotionArgs TouchInputMapper::dispatchMotion( int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords, const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, - float yPrecision, nsecs_t downTime, MotionClassification classification) { + float yPrecision, nsecs_t downTime, MotionClassification classification) const { std::vector<PointerCoords> pointerCoords; std::vector<PointerProperties> pointerProperties; uint32_t pointerCount = 0; @@ -3992,7 +3992,7 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, return true; } -std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() { +std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() const { return mParameters.hasAssociatedDisplay ? std::make_optional(mViewport.displayId) : std::nullopt; } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index eb4326fa56..96fc61b5ad 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -185,7 +185,7 @@ public: [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override; [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState( const StylusState& state) override; - std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override; + std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const override; protected: CursorButtonAccumulator mCursorButtonAccumulator; @@ -840,7 +840,7 @@ private: int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords, const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, - float yPrecision, nsecs_t downTime, MotionClassification classification); + float yPrecision, nsecs_t downTime, MotionClassification classification) const; // Returns if this touch device is a touch screen with an associated display. bool isTouchScreen(); diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 0c094e6cce..18a7102514 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -59,9 +59,11 @@ const bool DEBUG_TOUCHPAD_GESTURES = ANDROID_LOG_INFO); std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity, + bool accelerationEnabled, size_t propertySize) { - std::vector<AccelerationCurveSegment> segments = - createAccelerationCurveForPointerSensitivity(sensitivity); + std::vector<AccelerationCurveSegment> segments = accelerationEnabled + ? createAccelerationCurveForPointerSensitivity(sensitivity) + : createFlatAccelerationCurve(sensitivity); LOG_ALWAYS_FATAL_IF(propertySize < 4 * segments.size()); std::vector<double> output(propertySize, 0); @@ -358,12 +360,14 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, GesturesProp accelCurveProp = mPropertyProvider.getProperty("Pointer Accel Curve"); accelCurveProp.setRealValues( createAccelerationCurveForSensitivity(config.touchpadPointerSpeed, + config.touchpadAccelerationEnabled, accelCurveProp.getCount())); mPropertyProvider.getProperty("Use Custom Touchpad Scroll Accel Curve") .setBoolValues({true}); GesturesProp scrollCurveProp = mPropertyProvider.getProperty("Scroll Accel Curve"); scrollCurveProp.setRealValues( createAccelerationCurveForSensitivity(config.touchpadPointerSpeed, + config.touchpadAccelerationEnabled, scrollCurveProp.getCount())); mPropertyProvider.getProperty("Scroll X Out Scale").setRealValues({1.0}); mPropertyProvider.getProperty("Scroll Y Out Scale").setRealValues({1.0}); @@ -502,7 +506,7 @@ std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t return out; } -std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() { +std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() const { return mDisplayId; } @@ -510,4 +514,12 @@ std::optional<HardwareProperties> TouchpadInputMapper::getTouchpadHardwareProper return mHardwareProperties; } +std::optional<GesturesProp> TouchpadInputMapper::getGesturePropertyForTesting( + const std::string& name) { + if (!mPropertyProvider.hasProperty(name)) { + return std::nullopt; + } + return mPropertyProvider.getProperty(name); +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h index a2c4be9e50..9f53a7ba85 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -66,10 +66,12 @@ public: using MetricsIdentifier = std::tuple<uint16_t /*busId*/, uint16_t /*vendorId*/, uint16_t /*productId*/, uint16_t /*version*/>; - std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override; + std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const override; std::optional<HardwareProperties> getTouchpadHardwareProperties() override; + std::optional<GesturesProp> getGesturePropertyForTesting(const std::string& name); + private: void resetGestureInterpreter(nsecs_t when); explicit TouchpadInputMapper(InputDeviceContext& deviceContext, diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 827076a2cf..480e27604a 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -14,11 +14,15 @@ * limitations under the License. */ +#include "../Macros.h" + #include "gestures/GestureConverter.h" +#include <ios> #include <optional> #include <sstream> +#include <android-base/logging.h> #include <android-base/stringprintf.h> #include <com_android_input_flags.h> #include <ftl/enum.h> @@ -250,6 +254,18 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ const Gesture& gesture) { std::list<NotifyArgs> out = {}; + if (mCurrentClassification != MotionClassification::NONE) { + // Handling button changes during an ongoing gesture would be tricky, as we'd have to avoid + // sending duplicate DOWN events or premature UP events (e.g. if the gesture ended but the + // button was still down). It would also make handling touchpad events more difficult for + // apps, which would have to handle cases where e.g. a scroll gesture ends (and therefore + // the event lose the TWO_FINGER_SWIPE classification) but there isn't an UP because the + // button's still down. It's unclear how one should even handle button changes during most + // gestures, and they're probably accidental anyway. So, instead, just ignore them. + LOG(INFO) << "Ignoring button change because a gesture is ongoing."; + return out; + } + PointerCoords coords; coords.clear(); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); @@ -312,6 +328,15 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) { if (buttonsReleased & button) { uint32_t actionButton = gesturesButtonToMotionEventButton(button); + if (!(newButtonState & actionButton)) { + // We must have received the ButtonsChange gesture that put this button down during + // another gesture, and therefore dropped the BUTTON_PRESS action for it, or + // released the button when another gesture began during its press. Drop the + // BUTTON_RELEASE too to keep the stream consistent. + LOG(INFO) << "Dropping release event for button 0x" << std::hex << actionButton + << " as it wasn't in the button state."; + continue; + } newButtonState &= ~actionButton; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, newButtonState, /* pointerCount= */ 1, @@ -362,7 +387,7 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT std::list<NotifyArgs> out; PointerCoords& coords = mFakeFingerCoords[0]; if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { - out += exitHover(when, readTime); + out += prepareForFakeFingerGesture(when, readTime); mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE; coords.clear(); @@ -421,7 +446,7 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi std::list<NotifyArgs> out; mDownTime = when; mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE; - out += exitHover(when, readTime); + out += prepareForFakeFingerGesture(when, readTime); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /*actionButton=*/0, /*buttonState=*/0, /*pointerCount=*/1, &coords)); @@ -479,7 +504,7 @@ std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime // separate swipes with an appropriate lift event between them, so we don't have to worry // about the finger count changing mid-swipe. - out += exitHover(when, readTime); + out += prepareForFakeFingerGesture(when, readTime); mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE; @@ -567,9 +592,7 @@ std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START, "First pinch gesture does not have the START zoom state (%d instead).", gesture.details.pinch.zoom_state); - std::list<NotifyArgs> out; - - out += exitHover(when, readTime); + std::list<NotifyArgs> out = prepareForFakeFingerGesture(when, readTime); mCurrentClassification = MotionClassification::PINCH; mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX; @@ -644,6 +667,16 @@ std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime } } +std::list<NotifyArgs> GestureConverter::prepareForFakeFingerGesture(nsecs_t when, + nsecs_t readTime) { + std::list<NotifyArgs> out; + if (isPointerDown(mButtonState)) { + out += releaseAllButtons(when, readTime); + } + out += exitHover(when, readTime); + return out; +} + NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action) { PointerCoords coords; coords.clear(); diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index be76b61ce6..ae85e3a4ff 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -92,6 +92,8 @@ private: [[nodiscard]] std::list<NotifyArgs> enterHover(nsecs_t when, nsecs_t readTime); [[nodiscard]] std::list<NotifyArgs> exitHover(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> prepareForFakeFingerGesture(nsecs_t when, nsecs_t readTime); + NotifyMotionArgs makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action); NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, @@ -107,7 +109,7 @@ private: const bool mEnableNoFocusChange; bool mEnableSystemGestures{true}; - bool mThreeFingerTapShortcutEnabled; + bool mThreeFingerTapShortcutEnabled{false}; std::optional<ui::LogicalDisplayId> mDisplayId; FloatRect mBoundsInLogicalDisplay{}; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 600ae526f1..18d47f648b 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -40,14 +40,15 @@ cc_test { // defaults rather than including them as shared or static libraries. By doing so, the tests // will always run against the compiled version of the inputflinger code rather than the // version on the device. + "libinputdispatcher_defaults", "libinputflinger_base_defaults", + "libinputflinger_defaults", "libinputreader_defaults", "libinputreporter_defaults", - "libinputdispatcher_defaults", - "libinputflinger_defaults", ], srcs: [ ":inputdispatcher_common_test_sources", + "AndroidInputEventProtoConverter_test.cpp", "AnrTracker_test.cpp", "CapturedTouchpadEventConverter_test.cpp", "CursorInputMapper_test.cpp", @@ -62,16 +63,18 @@ cc_test { "HardwareStateConverter_test.cpp", "InputDeviceMetricsCollector_test.cpp", "InputDeviceMetricsSource_test.cpp", + "InputDispatcher_test.cpp", "InputMapperTest.cpp", - "InputProcessor_test.cpp", "InputProcessorConverter_test.cpp", - "InputDispatcher_test.cpp", + "InputProcessor_test.cpp", "InputReader_test.cpp", "InputTraceSession.cpp", "InputTracingTest.cpp", "InstrumentedInputReader.cpp", "JoystickInputMapper_test.cpp", + "KeyboardInputMapper_test.cpp", "LatencyTracker_test.cpp", + "MultiTouchInputMapper_test.cpp", "MultiTouchMotionAccumulator_test.cpp", "NotifyArgs_test.cpp", "PointerChoreographer_test.cpp", @@ -82,14 +85,12 @@ cc_test { "SlopController_test.cpp", "SwitchInputMapper_test.cpp", "SyncQueue_test.cpp", - "TimerProvider_test.cpp", "TestInputListener.cpp", + "TimerProvider_test.cpp", "TouchpadInputMapper_test.cpp", - "VibratorInputMapper_test.cpp", - "MultiTouchInputMapper_test.cpp", - "KeyboardInputMapper_test.cpp", "UinputDevice.cpp", "UnwantedInteractionBlocker_test.cpp", + "VibratorInputMapper_test.cpp", ], aidl: { include_dirs: [ @@ -109,7 +110,14 @@ cc_test { undefined: true, all_undefined: true, diag: { + cfi: true, + integer_overflow: true, + memtag_heap: true, undefined: true, + misc_undefined: [ + "all", + "bounds", + ], }, }, static_libs: [ @@ -121,8 +129,8 @@ cc_test { unit_test: true, }, test_suites: [ - "device-tests", "device-platinum-tests", + "device-tests", ], native_coverage: false, } diff --git a/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp b/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp new file mode 100644 index 0000000000..1fd6cee1eb --- /dev/null +++ b/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp @@ -0,0 +1,586 @@ +/* + * Copyright 2025 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 "../dispatcher/trace/AndroidInputEventProtoConverter.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android::inputdispatcher::trace { + +namespace { + +using testing::Return, testing::_; + +class MockProtoAxisValue { +public: + MOCK_METHOD(void, set_axis, (int32_t)); + MOCK_METHOD(void, set_value, (float)); +}; + +class MockProtoPointer { +public: + MOCK_METHOD(void, set_pointer_id, (uint32_t)); + MOCK_METHOD(void, set_tool_type, (int32_t)); + MOCK_METHOD(MockProtoAxisValue*, add_axis_value, ()); +}; + +class MockProtoMotion { +public: + MOCK_METHOD(void, set_event_id, (uint32_t)); + MOCK_METHOD(void, set_event_time_nanos, (int64_t)); + MOCK_METHOD(void, set_down_time_nanos, (int64_t)); + MOCK_METHOD(void, set_source, (uint32_t)); + MOCK_METHOD(void, set_action, (int32_t)); + MOCK_METHOD(void, set_device_id, (uint32_t)); + MOCK_METHOD(void, set_display_id, (uint32_t)); + MOCK_METHOD(void, set_classification, (int32_t)); + MOCK_METHOD(void, set_flags, (uint32_t)); + MOCK_METHOD(void, set_policy_flags, (uint32_t)); + MOCK_METHOD(void, set_button_state, (uint32_t)); + MOCK_METHOD(void, set_action_button, (uint32_t)); + MOCK_METHOD(void, set_cursor_position_x, (float)); + MOCK_METHOD(void, set_cursor_position_y, (float)); + MOCK_METHOD(void, set_meta_state, (uint32_t)); + MOCK_METHOD(void, set_precision_x, (float)); + MOCK_METHOD(void, set_precision_y, (float)); + MOCK_METHOD(MockProtoPointer*, add_pointer, ()); +}; + +class MockProtoKey { +public: + MOCK_METHOD(void, set_event_id, (uint32_t)); + MOCK_METHOD(void, set_event_time_nanos, (int64_t)); + MOCK_METHOD(void, set_down_time_nanos, (int64_t)); + MOCK_METHOD(void, set_source, (uint32_t)); + MOCK_METHOD(void, set_action, (int32_t)); + MOCK_METHOD(void, set_device_id, (uint32_t)); + MOCK_METHOD(void, set_display_id, (uint32_t)); + MOCK_METHOD(void, set_repeat_count, (uint32_t)); + MOCK_METHOD(void, set_flags, (uint32_t)); + MOCK_METHOD(void, set_policy_flags, (uint32_t)); + MOCK_METHOD(void, set_key_code, (uint32_t)); + MOCK_METHOD(void, set_scan_code, (uint32_t)); + MOCK_METHOD(void, set_meta_state, (uint32_t)); +}; + +class MockProtoDispatchPointer { +public: + MOCK_METHOD(void, set_pointer_id, (uint32_t)); + MOCK_METHOD(void, set_x_in_display, (float)); + MOCK_METHOD(void, set_y_in_display, (float)); + MOCK_METHOD(MockProtoAxisValue*, add_axis_value_in_window, ()); +}; + +class MockProtoDispatch { +public: + MOCK_METHOD(void, set_event_id, (uint32_t)); + MOCK_METHOD(void, set_vsync_id, (uint32_t)); + MOCK_METHOD(void, set_window_id, (uint32_t)); + MOCK_METHOD(void, set_resolved_flags, (uint32_t)); + MOCK_METHOD(MockProtoDispatchPointer*, add_dispatched_pointer, ()); +}; + +using TestProtoConverter = + AndroidInputEventProtoConverter<MockProtoMotion, MockProtoKey, MockProtoDispatch, + proto::AndroidInputEventConfig::Decoder>; + +TEST(AndroidInputEventProtoConverterTest, ToProtoMotionEvent) { + TracedMotionEvent event{}; + event.id = 1; + event.eventTime = 2; + event.downTime = 3; + event.source = AINPUT_SOURCE_MOUSE; + event.action = AMOTION_EVENT_ACTION_BUTTON_PRESS; + event.deviceId = 4; + event.displayId = ui::LogicalDisplayId(5); + event.classification = MotionClassification::PINCH; + event.flags = 6; + event.policyFlags = 7; + event.buttonState = 8; + event.actionButton = 9; + event.xCursorPosition = 10.0f; + event.yCursorPosition = 11.0f; + event.metaState = 12; + event.xPrecision = 13.0f; + event.yPrecision = 14.0f; + event.pointerProperties.emplace_back(PointerProperties{ + .id = 15, + .toolType = ToolType::MOUSE, + }); + event.pointerProperties.emplace_back(PointerProperties{ + .id = 16, + .toolType = ToolType::FINGER, + }); + event.pointerCoords.emplace_back(); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 17.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 18.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 19.0f); + event.pointerCoords.emplace_back(); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 20.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 21.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22.0f); + + testing::StrictMock<MockProtoMotion> proto; + testing::StrictMock<MockProtoPointer> pointer1; + testing::StrictMock<MockProtoPointer> pointer2; + testing::StrictMock<MockProtoAxisValue> axisValue1; + testing::StrictMock<MockProtoAxisValue> axisValue2; + testing::StrictMock<MockProtoAxisValue> axisValue3; + testing::StrictMock<MockProtoAxisValue> axisValue4; + testing::StrictMock<MockProtoAxisValue> axisValue5; + testing::StrictMock<MockProtoAxisValue> axisValue6; + + EXPECT_CALL(proto, set_event_id(1)); + EXPECT_CALL(proto, set_event_time_nanos(2)); + EXPECT_CALL(proto, set_down_time_nanos(3)); + EXPECT_CALL(proto, set_source(AINPUT_SOURCE_MOUSE)); + EXPECT_CALL(proto, set_action(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + EXPECT_CALL(proto, set_device_id(4)); + EXPECT_CALL(proto, set_display_id(5)); + EXPECT_CALL(proto, set_classification(AMOTION_EVENT_CLASSIFICATION_PINCH)); + EXPECT_CALL(proto, set_flags(6)); + EXPECT_CALL(proto, set_policy_flags(7)); + EXPECT_CALL(proto, set_button_state(8)); + EXPECT_CALL(proto, set_action_button(9)); + EXPECT_CALL(proto, set_cursor_position_x(10.0f)); + EXPECT_CALL(proto, set_cursor_position_y(11.0f)); + EXPECT_CALL(proto, set_meta_state(12)); + EXPECT_CALL(proto, set_precision_x(13.0f)); + EXPECT_CALL(proto, set_precision_y(14.0f)); + + EXPECT_CALL(proto, add_pointer()).WillOnce(Return(&pointer1)).WillOnce(Return(&pointer2)); + + EXPECT_CALL(pointer1, set_pointer_id(15)); + EXPECT_CALL(pointer1, set_tool_type(AMOTION_EVENT_TOOL_TYPE_MOUSE)); + EXPECT_CALL(pointer1, add_axis_value()) + .WillOnce(Return(&axisValue1)) + .WillOnce(Return(&axisValue2)) + .WillOnce(Return(&axisValue3)); + EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_X)); + EXPECT_CALL(axisValue1, set_value(17.0f)); + EXPECT_CALL(axisValue2, set_axis(AMOTION_EVENT_AXIS_Y)); + EXPECT_CALL(axisValue2, set_value(18.0f)); + EXPECT_CALL(axisValue3, set_axis(AMOTION_EVENT_AXIS_PRESSURE)); + EXPECT_CALL(axisValue3, set_value(19.0f)); + + EXPECT_CALL(pointer2, set_pointer_id(16)); + EXPECT_CALL(pointer2, set_tool_type(AMOTION_EVENT_TOOL_TYPE_FINGER)); + EXPECT_CALL(pointer2, add_axis_value()) + .WillOnce(Return(&axisValue4)) + .WillOnce(Return(&axisValue5)) + .WillOnce(Return(&axisValue6)); + EXPECT_CALL(axisValue4, set_axis(AMOTION_EVENT_AXIS_X)); + EXPECT_CALL(axisValue4, set_value(20.0f)); + EXPECT_CALL(axisValue5, set_axis(AMOTION_EVENT_AXIS_Y)); + EXPECT_CALL(axisValue5, set_value(21.0f)); + EXPECT_CALL(axisValue6, set_axis(AMOTION_EVENT_AXIS_PRESSURE)); + EXPECT_CALL(axisValue6, set_value(22.0f)); + + TestProtoConverter::toProtoMotionEvent(event, proto, /*isRedacted=*/false); +} + +TEST(AndroidInputEventProtoConverterTest, ToProtoMotionEvent_Redacted) { + TracedMotionEvent event{}; + event.id = 1; + event.eventTime = 2; + event.downTime = 3; + event.source = AINPUT_SOURCE_MOUSE; + event.action = AMOTION_EVENT_ACTION_BUTTON_PRESS; + event.deviceId = 4; + event.displayId = ui::LogicalDisplayId(5); + event.classification = MotionClassification::PINCH; + event.flags = 6; + event.policyFlags = 7; + event.buttonState = 8; + event.actionButton = 9; + event.xCursorPosition = 10.0f; + event.yCursorPosition = 11.0f; + event.metaState = 12; + event.xPrecision = 13.0f; + event.yPrecision = 14.0f; + event.pointerProperties.emplace_back(PointerProperties{ + .id = 15, + .toolType = ToolType::MOUSE, + }); + event.pointerProperties.emplace_back(PointerProperties{ + .id = 16, + .toolType = ToolType::FINGER, + }); + event.pointerCoords.emplace_back(); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 17.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 18.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 19.0f); + event.pointerCoords.emplace_back(); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 20.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 21.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22.0f); + + testing::StrictMock<MockProtoMotion> proto; + testing::StrictMock<MockProtoPointer> pointer1; + testing::StrictMock<MockProtoPointer> pointer2; + testing::StrictMock<MockProtoAxisValue> axisValue1; + testing::StrictMock<MockProtoAxisValue> axisValue2; + testing::StrictMock<MockProtoAxisValue> axisValue3; + testing::StrictMock<MockProtoAxisValue> axisValue4; + testing::StrictMock<MockProtoAxisValue> axisValue5; + testing::StrictMock<MockProtoAxisValue> axisValue6; + + EXPECT_CALL(proto, set_event_id(1)); + EXPECT_CALL(proto, set_event_time_nanos(2)); + EXPECT_CALL(proto, set_down_time_nanos(3)); + EXPECT_CALL(proto, set_source(AINPUT_SOURCE_MOUSE)); + EXPECT_CALL(proto, set_action(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + EXPECT_CALL(proto, set_device_id(4)); + EXPECT_CALL(proto, set_display_id(5)); + EXPECT_CALL(proto, set_classification(AMOTION_EVENT_CLASSIFICATION_PINCH)); + EXPECT_CALL(proto, set_flags(6)); + EXPECT_CALL(proto, set_policy_flags(7)); + EXPECT_CALL(proto, set_button_state(8)); + EXPECT_CALL(proto, set_action_button(9)); + + EXPECT_CALL(proto, add_pointer()).WillOnce(Return(&pointer1)).WillOnce(Return(&pointer2)); + + EXPECT_CALL(pointer1, set_pointer_id(15)); + EXPECT_CALL(pointer1, set_tool_type(AMOTION_EVENT_TOOL_TYPE_MOUSE)); + EXPECT_CALL(pointer1, add_axis_value()) + .WillOnce(Return(&axisValue1)) + .WillOnce(Return(&axisValue2)) + .WillOnce(Return(&axisValue3)); + EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_X)); + EXPECT_CALL(axisValue2, set_axis(AMOTION_EVENT_AXIS_Y)); + EXPECT_CALL(axisValue3, set_axis(AMOTION_EVENT_AXIS_PRESSURE)); + + EXPECT_CALL(pointer2, set_pointer_id(16)); + EXPECT_CALL(pointer2, set_tool_type(AMOTION_EVENT_TOOL_TYPE_FINGER)); + EXPECT_CALL(pointer2, add_axis_value()) + .WillOnce(Return(&axisValue4)) + .WillOnce(Return(&axisValue5)) + .WillOnce(Return(&axisValue6)); + EXPECT_CALL(axisValue4, set_axis(AMOTION_EVENT_AXIS_X)); + EXPECT_CALL(axisValue5, set_axis(AMOTION_EVENT_AXIS_Y)); + EXPECT_CALL(axisValue6, set_axis(AMOTION_EVENT_AXIS_PRESSURE)); + + // Redacted fields + EXPECT_CALL(proto, set_meta_state(_)).Times(0); + EXPECT_CALL(proto, set_cursor_position_x(_)).Times(0); + EXPECT_CALL(proto, set_cursor_position_y(_)).Times(0); + EXPECT_CALL(proto, set_precision_x(_)).Times(0); + EXPECT_CALL(proto, set_precision_y(_)).Times(0); + EXPECT_CALL(axisValue1, set_value(_)).Times(0); + EXPECT_CALL(axisValue2, set_value(_)).Times(0); + EXPECT_CALL(axisValue3, set_value(_)).Times(0); + EXPECT_CALL(axisValue4, set_value(_)).Times(0); + EXPECT_CALL(axisValue5, set_value(_)).Times(0); + EXPECT_CALL(axisValue6, set_value(_)).Times(0); + + TestProtoConverter::toProtoMotionEvent(event, proto, /*isRedacted=*/true); +} + +// Test any special handling for zero values for pointer events. +TEST(AndroidInputEventProtoConverterTest, ToProtoMotionEvent_ZeroValues) { + TracedMotionEvent event{}; + event.id = 0; + event.eventTime = 0; + event.downTime = 0; + event.source = AINPUT_SOURCE_MOUSE; + event.action = AMOTION_EVENT_ACTION_BUTTON_PRESS; + event.deviceId = 0; + event.displayId = ui::LogicalDisplayId(0); + event.classification = {}; + event.flags = 0; + event.policyFlags = 0; + event.buttonState = 0; + event.actionButton = 0; + event.xCursorPosition = 0.0f; + event.yCursorPosition = 0.0f; + event.metaState = 0; + event.xPrecision = 0.0f; + event.yPrecision = 0.0f; + event.pointerProperties.emplace_back(PointerProperties{ + .id = 0, + .toolType = ToolType::MOUSE, + }); + event.pointerProperties.emplace_back(PointerProperties{ + .id = 1, + .toolType = ToolType::FINGER, + }); + // Zero values for x and y axes are always traced for pointer events. + // However, zero values for other axes may not necessarily be traced. + event.pointerCoords.emplace_back(); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 0.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 1.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); + event.pointerCoords.emplace_back(); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 0.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 0.0f); + event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); + + testing::StrictMock<MockProtoMotion> proto; + testing::StrictMock<MockProtoPointer> pointer1; + testing::StrictMock<MockProtoPointer> pointer2; + testing::StrictMock<MockProtoAxisValue> axisValue1; + testing::StrictMock<MockProtoAxisValue> axisValue2; + testing::StrictMock<MockProtoAxisValue> axisValue3; + testing::StrictMock<MockProtoAxisValue> axisValue4; + + EXPECT_CALL(proto, set_event_id(0)); + EXPECT_CALL(proto, set_event_time_nanos(0)); + EXPECT_CALL(proto, set_down_time_nanos(0)); + EXPECT_CALL(proto, set_source(AINPUT_SOURCE_MOUSE)); + EXPECT_CALL(proto, set_action(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + EXPECT_CALL(proto, set_device_id(0)); + EXPECT_CALL(proto, set_display_id(0)); + EXPECT_CALL(proto, set_classification(0)); + EXPECT_CALL(proto, set_flags(0)); + EXPECT_CALL(proto, set_policy_flags(0)); + EXPECT_CALL(proto, set_button_state(0)); + EXPECT_CALL(proto, set_action_button(0)); + EXPECT_CALL(proto, set_cursor_position_x(0.0f)); + EXPECT_CALL(proto, set_cursor_position_y(0.0f)); + EXPECT_CALL(proto, set_meta_state(0)); + EXPECT_CALL(proto, set_precision_x(0.0f)); + EXPECT_CALL(proto, set_precision_y(0.0f)); + + EXPECT_CALL(proto, add_pointer()).WillOnce(Return(&pointer1)).WillOnce(Return(&pointer2)); + + EXPECT_CALL(pointer1, set_pointer_id(0)); + EXPECT_CALL(pointer1, set_tool_type(AMOTION_EVENT_TOOL_TYPE_MOUSE)); + EXPECT_CALL(pointer1, add_axis_value()) + .WillOnce(Return(&axisValue1)) + .WillOnce(Return(&axisValue2)); + EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_X)); + EXPECT_CALL(axisValue1, set_value(0.0f)); + EXPECT_CALL(axisValue2, set_axis(AMOTION_EVENT_AXIS_Y)); + EXPECT_CALL(axisValue2, set_value(1.0f)); + + EXPECT_CALL(pointer2, set_pointer_id(1)); + EXPECT_CALL(pointer2, set_tool_type(AMOTION_EVENT_TOOL_TYPE_FINGER)); + EXPECT_CALL(pointer2, add_axis_value()) + .WillOnce(Return(&axisValue3)) + .WillOnce(Return(&axisValue4)); + EXPECT_CALL(axisValue3, set_axis(AMOTION_EVENT_AXIS_X)); + EXPECT_CALL(axisValue3, set_value(0.0f)); + EXPECT_CALL(axisValue4, set_axis(AMOTION_EVENT_AXIS_Y)); + EXPECT_CALL(axisValue4, set_value(0.0f)); + + TestProtoConverter::toProtoMotionEvent(event, proto, /*isRedacted=*/false); +} + +TEST(AndroidInputEventProtoConverterTest, ToProtoKeyEvent) { + TracedKeyEvent event{}; + event.id = 1; + event.eventTime = 2; + event.downTime = 3; + event.source = AINPUT_SOURCE_KEYBOARD; + event.action = AKEY_EVENT_ACTION_DOWN; + event.deviceId = 4; + event.displayId = ui::LogicalDisplayId(5); + event.repeatCount = 6; + event.flags = 7; + event.policyFlags = 8; + event.keyCode = 9; + event.scanCode = 10; + event.metaState = 11; + + testing::StrictMock<MockProtoKey> proto; + + EXPECT_CALL(proto, set_event_id(1)); + EXPECT_CALL(proto, set_event_time_nanos(2)); + EXPECT_CALL(proto, set_down_time_nanos(3)); + EXPECT_CALL(proto, set_source(AINPUT_SOURCE_KEYBOARD)); + EXPECT_CALL(proto, set_action(AKEY_EVENT_ACTION_DOWN)); + EXPECT_CALL(proto, set_device_id(4)); + EXPECT_CALL(proto, set_display_id(5)); + EXPECT_CALL(proto, set_repeat_count(6)); + EXPECT_CALL(proto, set_flags(7)); + EXPECT_CALL(proto, set_policy_flags(8)); + EXPECT_CALL(proto, set_key_code(9)); + EXPECT_CALL(proto, set_scan_code(10)); + EXPECT_CALL(proto, set_meta_state(11)); + + TestProtoConverter::toProtoKeyEvent(event, proto, /*isRedacted=*/false); +} + +TEST(AndroidInputEventProtoConverterTest, ToProtoKeyEvent_Redacted) { + TracedKeyEvent event{}; + event.id = 1; + event.eventTime = 2; + event.downTime = 3; + event.source = AINPUT_SOURCE_KEYBOARD; + event.action = AKEY_EVENT_ACTION_DOWN; + event.deviceId = 4; + event.displayId = ui::LogicalDisplayId(5); + event.repeatCount = 6; + event.flags = 7; + event.policyFlags = 8; + event.keyCode = 9; + event.scanCode = 10; + event.metaState = 11; + + testing::StrictMock<MockProtoKey> proto; + + EXPECT_CALL(proto, set_event_id(1)); + EXPECT_CALL(proto, set_event_time_nanos(2)); + EXPECT_CALL(proto, set_down_time_nanos(3)); + EXPECT_CALL(proto, set_source(AINPUT_SOURCE_KEYBOARD)); + EXPECT_CALL(proto, set_action(AKEY_EVENT_ACTION_DOWN)); + EXPECT_CALL(proto, set_device_id(4)); + EXPECT_CALL(proto, set_display_id(5)); + EXPECT_CALL(proto, set_repeat_count(6)); + EXPECT_CALL(proto, set_flags(7)); + EXPECT_CALL(proto, set_policy_flags(8)); + + // Redacted fields + EXPECT_CALL(proto, set_key_code(_)).Times(0); + EXPECT_CALL(proto, set_scan_code(_)).Times(0); + EXPECT_CALL(proto, set_meta_state(_)).Times(0); + + TestProtoConverter::toProtoKeyEvent(event, proto, /*isRedacted=*/true); +} + +TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_IdentityTransform) { + TracedMotionEvent motion{}; + motion.pointerProperties.emplace_back(PointerProperties{ + .id = 4, + .toolType = ToolType::MOUSE, + }); + motion.pointerCoords.emplace_back(); + motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 5.0f); + motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f); + + WindowDispatchArgs args{}; + args.eventEntry = motion; + args.vsyncId = 1; + args.windowId = 2; + args.resolvedFlags = 3; + args.rawTransform = ui::Transform{}; + args.transform = ui::Transform{}; + + testing::StrictMock<MockProtoDispatch> proto; + testing::StrictMock<MockProtoDispatchPointer> pointer; + + EXPECT_CALL(proto, set_event_id(0)); + EXPECT_CALL(proto, set_vsync_id(1)); + EXPECT_CALL(proto, set_window_id(2)); + EXPECT_CALL(proto, set_resolved_flags(3)); + EXPECT_CALL(proto, add_dispatched_pointer()).WillOnce(Return(&pointer)); + EXPECT_CALL(pointer, set_pointer_id(4)); + + // Since we are using identity transforms, the axis values will be identical to those in the + // traced event, so they should not be traced here. + EXPECT_CALL(pointer, add_axis_value_in_window()).Times(0); + EXPECT_CALL(pointer, set_x_in_display(_)).Times(0); + EXPECT_CALL(pointer, set_y_in_display(_)).Times(0); + + TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/false); +} + +TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_CustomTransform) { + TracedMotionEvent motion{}; + motion.pointerProperties.emplace_back(PointerProperties{ + .id = 4, + .toolType = ToolType::MOUSE, + }); + motion.pointerCoords.emplace_back(); + motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 8.0f); + motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f); + + WindowDispatchArgs args{}; + args.eventEntry = motion; + args.vsyncId = 1; + args.windowId = 2; + args.resolvedFlags = 3; + args.rawTransform.set(2, 0, 0, 0.5); + args.transform.set(1.0, 0, 0, 0.5); + + testing::StrictMock<MockProtoDispatch> proto; + testing::StrictMock<MockProtoDispatchPointer> pointer; + testing::StrictMock<MockProtoAxisValue> axisValue1; + + EXPECT_CALL(proto, set_event_id(0)); + EXPECT_CALL(proto, set_vsync_id(1)); + EXPECT_CALL(proto, set_window_id(2)); + EXPECT_CALL(proto, set_resolved_flags(3)); + EXPECT_CALL(proto, add_dispatched_pointer()).WillOnce(Return(&pointer)); + EXPECT_CALL(pointer, set_pointer_id(4)); + + // Only the transformed axis-values that differ from the traced event will be traced. + EXPECT_CALL(pointer, add_axis_value_in_window()).WillOnce(Return(&axisValue1)); + EXPECT_CALL(pointer, set_x_in_display(16.0f)); // MotionEvent::getRawX + EXPECT_CALL(pointer, set_y_in_display(3.0f)); // MotionEvent::getRawY + + EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_Y)); + EXPECT_CALL(axisValue1, set_value(3.0f)); + + TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/false); +} + +TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_Redacted) { + TracedMotionEvent motion{}; + motion.pointerProperties.emplace_back(PointerProperties{ + .id = 4, + .toolType = ToolType::MOUSE, + }); + motion.pointerCoords.emplace_back(); + motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 5.0f); + motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f); + + WindowDispatchArgs args{}; + args.eventEntry = motion; + args.vsyncId = 1; + args.windowId = 2; + args.resolvedFlags = 3; + args.rawTransform = ui::Transform{}; + args.transform = ui::Transform{}; + + testing::StrictMock<MockProtoDispatch> proto; + + EXPECT_CALL(proto, set_event_id(0)); + EXPECT_CALL(proto, set_vsync_id(1)); + EXPECT_CALL(proto, set_window_id(2)); + EXPECT_CALL(proto, set_resolved_flags(3)); + + // Redacted fields + EXPECT_CALL(proto, add_dispatched_pointer()).Times(0); + + TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/true); +} + +TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Key) { + TracedKeyEvent key{}; + + WindowDispatchArgs args{}; + args.eventEntry = key; + args.vsyncId = 1; + args.windowId = 2; + args.resolvedFlags = 3; + args.rawTransform = ui::Transform{}; + args.transform = ui::Transform{}; + + testing::StrictMock<MockProtoDispatch> proto; + + EXPECT_CALL(proto, set_event_id(0)); + EXPECT_CALL(proto, set_vsync_id(1)); + EXPECT_CALL(proto, set_window_id(2)); + EXPECT_CALL(proto, set_resolved_flags(3)); + + TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/true); +} + +} // namespace + +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index 18e0b309ba..594ee3b579 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -141,9 +141,9 @@ namespace vd_flags = android::companion::virtualdevice::flags; */ class CursorInputMapperUnitTestBase : public InputMapperUnitTest { protected: - void SetUp() override { SetUpWithBus(BUS_USB); } - void SetUpWithBus(int bus) override { - InputMapperUnitTest::SetUpWithBus(bus); + void SetUp() override { SetUp(BUS_USB, /*isExternal=*/false); } + void SetUp(int bus, bool isExternal) override { + InputMapperUnitTest::SetUp(bus, isExternal); // Current scan code state - all keys are UP by default setScanCodeState(KeyState::UP, @@ -1142,7 +1142,9 @@ constexpr nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); class BluetoothCursorInputMapperUnitTest : public CursorInputMapperUnitTestBase { protected: - void SetUp() override { SetUpWithBus(BUS_BLUETOOTH); } + void SetUp() override { + CursorInputMapperUnitTestBase::SetUp(BUS_BLUETOOTH, /*isExternal=*/true); + } }; TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) { diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 67b1e8c249..5a14f4bdfe 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -31,6 +31,30 @@ static const int HW_TIMEOUT_MULTIPLIER = base::GetIntProperty("ro.hw_timeout_mul } // namespace +DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height, + ui::Rotation orientation, bool isActive, const std::string& uniqueId, + std::optional<uint8_t> physicalPort, ViewportType type) { + const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270; + DisplayViewport v; + v.displayId = displayId; + v.orientation = orientation; + v.logicalLeft = 0; + v.logicalTop = 0; + v.logicalRight = isRotated ? height : width; + v.logicalBottom = isRotated ? width : height; + v.physicalLeft = 0; + v.physicalTop = 0; + v.physicalRight = isRotated ? height : width; + v.physicalBottom = isRotated ? width : height; + v.deviceWidth = isRotated ? height : width; + v.deviceHeight = isRotated ? width : height; + v.isActive = isActive; + v.uniqueId = uniqueId; + v.physicalPort = physicalPort; + v.type = type; + return v; +}; + void FakeInputReaderPolicy::assertInputDevicesChanged() { waitForInputDevices( [](bool devicesChanged) { @@ -115,33 +139,6 @@ void FakeInputReaderPolicy::addDisplayViewport(DisplayViewport viewport) { mConfig.setDisplayViewports(mViewports); } -void FakeInputReaderPolicy::addDisplayViewport(ui::LogicalDisplayId displayId, int32_t width, - int32_t height, ui::Rotation orientation, - bool isActive, const std::string& uniqueId, - std::optional<uint8_t> physicalPort, - ViewportType type) { - const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270; - DisplayViewport v; - v.displayId = displayId; - v.orientation = orientation; - v.logicalLeft = 0; - v.logicalTop = 0; - v.logicalRight = isRotated ? height : width; - v.logicalBottom = isRotated ? width : height; - v.physicalLeft = 0; - v.physicalTop = 0; - v.physicalRight = isRotated ? height : width; - v.physicalBottom = isRotated ? width : height; - v.deviceWidth = isRotated ? height : width; - v.deviceHeight = isRotated ? width : height; - v.isActive = isActive; - v.uniqueId = uniqueId; - v.physicalPort = physicalPort; - v.type = type; - - addDisplayViewport(v); -} - bool FakeInputReaderPolicy::updateViewport(const DisplayViewport& viewport) { size_t count = mViewports.size(); for (size_t i = 0; i < count; i++) { diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 42c956789b..9dce31a8a6 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -31,6 +31,10 @@ namespace android { +DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height, + ui::Rotation orientation, bool isActive, const std::string& uniqueId, + std::optional<uint8_t> physicalPort, ViewportType type); + class FakeInputReaderPolicy : public InputReaderPolicyInterface { protected: virtual ~FakeInputReaderPolicy() {} @@ -50,9 +54,6 @@ public: std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const; std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const; void addDisplayViewport(DisplayViewport viewport); - void addDisplayViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height, - ui::Rotation orientation, bool isActive, const std::string& uniqueId, - std::optional<uint8_t> physicalPort, ViewportType type); bool updateViewport(const DisplayViewport& viewport); void addExcludedDeviceName(const std::string& deviceName); void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort); diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 8fa439d633..fd9884b234 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -15,11 +15,15 @@ */ #include <memory> +#include <tuple> +#include <android-base/result-gmock.h> +#include <android-base/result.h> #include <com_android_input_flags.h> #include <flag_macros.h> #include <gestures/GestureConverter.h> #include <gtest/gtest.h> +#include <input/InputVerifier.h> #include "FakeEventHub.h" #include "FakeInputReaderPolicy.h" @@ -43,8 +47,12 @@ const auto TOUCHPAD_PALM_REJECTION = const auto TOUCHPAD_PALM_REJECTION_V2 = ACONFIG_FLAG(input_flags, enable_v2_touchpad_typing_palm_rejection); +constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2; +constexpr stime_t GESTURE_TIME = ARBITRARY_GESTURE_TIME; + } // namespace +using android::base::testing::Ok; using testing::AllOf; using testing::Each; using testing::ElementsAre; @@ -55,9 +63,8 @@ class GestureConverterTest : public testing::Test { protected: static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; static constexpr int32_t EVENTHUB_ID = 1; - static constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2; - void SetUp() { + GestureConverterTest() { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = sp<FakeInputReaderPolicy>::make(); mFakeListener = std::make_unique<TestInputListener>(); @@ -1698,4 +1705,135 @@ TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); } +/** + * Tests that the event stream output by the converter remains consistent when converting sequences + * of Gestures interleaved with button presses in various ways. Takes tuples of three Gestures: one + * that starts the gesture sequence, one that continues it (which may or may not be used in a + * particular test case), and one that ends it. + */ +class GestureConverterConsistencyTest + : public GestureConverterTest, + public testing::WithParamInterface<std::tuple<Gesture, Gesture, Gesture>> { +protected: + GestureConverterConsistencyTest() + : GestureConverterTest(), + mParamStartGesture(std::get<0>(GetParam())), + mParamContinueGesture(std::get<1>(GetParam())), + mParamEndGesture(std::get<2>(GetParam())), + mDeviceContext(*mDevice, EVENTHUB_ID), + mConverter(*mReader->getContext(), mDeviceContext, DEVICE_ID), + mVerifier("Test verifier") { + mConverter.setDisplayId(ui::LogicalDisplayId::DEFAULT); + } + + base::Result<void> processMotionArgs(NotifyMotionArgs arg) { + return mVerifier.processMovement(arg.deviceId, arg.source, arg.action, + arg.getPointerCount(), arg.pointerProperties.data(), + arg.pointerCoords.data(), arg.flags); + } + + void verifyArgsFromGesture(const Gesture& gesture, size_t gestureIndex) { + std::list<NotifyArgs> args = + mConverter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, gesture); + for (const NotifyArgs& notifyArg : args) { + const NotifyMotionArgs& arg = std::get<NotifyMotionArgs>(notifyArg); + ASSERT_THAT(processMotionArgs(arg), Ok()) + << "when processing " << arg.dump() << "\nfrom gesture " << gestureIndex << ": " + << gesture.String(); + } + } + + void verifyArgsFromGestures(const std::vector<Gesture>& gestures) { + for (size_t i = 0; i < gestures.size(); i++) { + ASSERT_NO_FATAL_FAILURE(verifyArgsFromGesture(gestures[i], i)); + } + } + + Gesture mParamStartGesture; + Gesture mParamContinueGesture; + Gesture mParamEndGesture; + + InputDeviceContext mDeviceContext; + GestureConverter mConverter; + InputVerifier mVerifier; +}; + +TEST_P(GestureConverterConsistencyTest, ButtonChangesDuringGesture) { + verifyArgsFromGestures({ + mParamStartGesture, + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false), + mParamContinueGesture, + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_NONE, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false), + mParamEndGesture, + }); +} + +TEST_P(GestureConverterConsistencyTest, ButtonDownDuringGestureAndUpAfterEnd) { + verifyArgsFromGestures({ + mParamStartGesture, + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false), + mParamContinueGesture, + mParamEndGesture, + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_NONE, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false), + }); +} + +TEST_P(GestureConverterConsistencyTest, GestureStartAndEndDuringButtonDown) { + verifyArgsFromGestures({ + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false), + mParamStartGesture, + mParamContinueGesture, + mParamEndGesture, + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_NONE, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false), + }); +} + +TEST_P(GestureConverterConsistencyTest, GestureStartsWhileButtonDownAndEndsAfterUp) { + verifyArgsFromGestures({ + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false), + mParamStartGesture, + mParamContinueGesture, + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_NONE, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false), + mParamEndGesture, + }); +} + +TEST_P(GestureConverterConsistencyTest, TapToClickDuringGesture) { + verifyArgsFromGestures({ + mParamStartGesture, + Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false), + mParamEndGesture, + }); +} + +INSTANTIATE_TEST_SUITE_P( + GestureAndButtonInterleavings, GestureConverterConsistencyTest, + testing::Values( + std::make_tuple(Gesture(kGestureScroll, GESTURE_TIME, GESTURE_TIME, 0, -10), + Gesture(kGestureScroll, GESTURE_TIME, GESTURE_TIME, 0, -5), + Gesture(kGestureFling, GESTURE_TIME, GESTURE_TIME, 1, 1, + GESTURES_FLING_START)), + std::make_tuple(Gesture(kGestureSwipe, GESTURE_TIME, GESTURE_TIME, 0, -10), + Gesture(kGestureSwipe, GESTURE_TIME, GESTURE_TIME, 0, 5), + Gesture(kGestureSwipeLift, GESTURE_TIME, GESTURE_TIME)), + std::make_tuple(Gesture(kGestureFourFingerSwipe, GESTURE_TIME, GESTURE_TIME, 0, + -10), + Gesture(kGestureFourFingerSwipe, GESTURE_TIME, GESTURE_TIME, 0, 5), + Gesture(kGestureFourFingerSwipeLift, GESTURE_TIME, GESTURE_TIME)), + std::make_tuple(Gesture(kGesturePinch, GESTURE_TIME, GESTURE_TIME, + /*dz=*/1, GESTURES_ZOOM_START), + Gesture(kGesturePinch, GESTURE_TIME, GESTURE_TIME, + /*dz=*/0.8, GESTURES_ZOOM_UPDATE), + Gesture(kGesturePinch, GESTURE_TIME, GESTURE_TIME, + /*dz=*/1, GESTURES_ZOOM_END)))); + } // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5c82319615..368db1b5c4 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -8101,6 +8101,17 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode); ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState); ASSERT_EQ(0, verifiedKey.repeatCount); + + // InputEvent and subclasses don't have a virtual destructor and only + // InputEvent's destructor gets called when `verified` goes out of scope, + // even if `verifyInputEvent` returns an object of a subclass. To fix this, + // we should either consider using std::variant in some way, or introduce an + // intermediate POD data structure that we will put the data into just prior + // to signing. Adding virtual functions to these classes is undesirable as + // the bytes in these objects are getting signed. As a temporary fix, cast + // the pointer to the correct class (which is statically known) before + // destruction. + delete (VerifiedKeyEvent*)verified.release(); } TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { @@ -8148,6 +8159,10 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos); EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState); EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState); + + // Cast to the correct type before destruction. See explanation at the end + // of the VerifyInputEvent_KeyEvent test. + delete (VerifiedMotionEvent*)verified.release(); } /** @@ -14923,4 +14938,195 @@ TEST_F(InputDispatcherObscuredFlagTest, StylusHoverObscuredTest) { mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithFlags(0))); } +class TransferOrDontTransferFixture : public InputDispatcherTest, + public ::testing::WithParamInterface<bool> { +public: + void SetUp() override { + InputDispatcherTest::SetUp(); + + std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>(); + mFromWindow = + sp<FakeWindowHandle>::make(app, mDispatcher, "From", ui::LogicalDisplayId::DEFAULT); + mToWindow = + sp<FakeWindowHandle>::make(app, mDispatcher, "To", ui::LogicalDisplayId::DEFAULT); + + mDispatcher->onWindowInfosChanged( + {{*mFromWindow->getInfo(), *mToWindow->getInfo()}, {}, 0, 0}); + } + +protected: + sp<FakeWindowHandle> mFromWindow; + sp<FakeWindowHandle> mToWindow; +}; + +// Start a touch gesture and then continue hovering the mouse at the same time. +// After the mouse is hovering, invoke transferTouch API. Check the events that +// are received by each of the windows. +TEST_P(TransferOrDontTransferFixture, TouchDownAndMouseHover) { + SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false); + + const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC); + const int32_t mouseDeviceId = 6; + const int32_t touchDeviceId = 4; + + // Send touch down to the first window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 10) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100)) + .build()); + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Send touch move to the first window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 20) + .pointer(PointerBuilder(0, ToolType::FINGER).x(110).y(100)) + .build()); + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + + // Start mouse hover on the first window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .downTime(baseTime + 30) + .eventTime(baseTime + 30) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(200).y(200)) + .build()); + + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + if (GetParam()) { + // Call transferTouchGesture + const bool transferred = + mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken()); + ASSERT_TRUE(transferred); + + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + // b/382473355: For some reason, mToWindow also receives HOVER_EXIT first + mToWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Further touch events should be delivered to mTowindow (?) + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 40) + .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100)) + .build()); + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 50) + .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100)) + .build()); + // b/382473355: Even though the window got ACTION_DOWN, it's no longer receiving the + // remainder of the touch gesture. + + mFromWindow->assertNoEvents(); + mToWindow->assertNoEvents(); + } else { + // Don't call transferTouchGesture + + // Further touch events should be dropped + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 40) + .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100)) + .build()); + mFromWindow->assertNoEvents(); + mToWindow->assertNoEvents(); + } +} + +TEST_P(TransferOrDontTransferFixture, MouseAndTouchTransferSimultaneousMultiDevice) { + SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true); + + const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC); + const int32_t mouseDeviceId = 6; + const int32_t touchDeviceId = 4; + + // Send touch down to the 'From' window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 10) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100)) + .build()); + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Send touch move to the 'From' window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 20) + .pointer(PointerBuilder(0, ToolType::FINGER).x(110).y(100)) + .build()); + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + + // Start mouse hover on the 'From' window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .downTime(baseTime + 30) + .eventTime(baseTime + 30) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(200).y(200)) + .build()); + + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + if (GetParam()) { + // Call transferTouchGesture + const bool transferred = + mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken()); + ASSERT_TRUE(transferred); + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Further touch events should be delivered to mToWindow + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 40) + .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100)) + .build()); + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 50) + .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100)) + .build()); + // b/382473355: Even though the window got ACTION_DOWN, it's receiving another DOWN! + mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + mToWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + mToWindow->consumeMotionEvent(WithMotionAction(ACTION_UP)); + + mFromWindow->assertNoEvents(); + mToWindow->assertNoEvents(); + } else { + // Don't call transferTouchGesture + + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .downTime(baseTime + 10) + .eventTime(baseTime + 40) + .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100)) + .build()); + mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + mFromWindow->assertNoEvents(); + mToWindow->assertNoEvents(); + } +} + +INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, testing::Bool()); + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index 8235c90da6..652a6580dc 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -30,7 +30,7 @@ using testing::NiceMock; using testing::Return; using testing::ReturnRef; -void InputMapperUnitTest::SetUpWithBus(int bus) { +void InputMapperUnitTest::SetUp(int bus, bool isExternal) { mFakePolicy = sp<FakeInputReaderPolicy>::make(); EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get())); @@ -46,8 +46,9 @@ void InputMapperUnitTest::SetUpWithBus(int bus) { return mPropertyMap; }); - mDevice = std::make_unique<NiceMock<MockInputDevice>>(&mMockInputReaderContext, DEVICE_ID, - /*generation=*/2, mIdentifier); + mDevice = + std::make_unique<NiceMock<MockInputDevice>>(&mMockInputReaderContext, DEVICE_ID, + /*generation=*/2, mIdentifier, isExternal); ON_CALL((*mDevice), getConfiguration).WillByDefault(ReturnRef(mPropertyMap)); mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID); } @@ -185,8 +186,10 @@ void InputMapperTest::setDisplayInfoAndReconfigure(ui::LogicalDisplayId displayI const std::string& uniqueId, std::optional<uint8_t> physicalPort, ViewportType viewportType) { - mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true, - uniqueId, physicalPort, viewportType); + DisplayViewport viewport = + createViewport(displayId, width, height, orientation, /* isActive= */ true, uniqueId, + physicalPort, viewportType); + mFakePolicy->addDisplayViewport(viewport); configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); } diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index 10ef6f1660..edc87a32b0 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -40,8 +40,8 @@ class InputMapperUnitTest : public testing::Test { protected: static constexpr int32_t EVENTHUB_ID = 1; static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; - virtual void SetUp() override { SetUpWithBus(0); } - virtual void SetUpWithBus(int bus); + virtual void SetUp() override { SetUp(/*bus=*/0, /*isExternal=*/false); } + virtual void SetUp(int bus, bool isExternal); void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution, int32_t flat = 0, int32_t fuzz = 0); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 470e65b8b5..a8e4736aea 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -385,30 +385,29 @@ TEST_F(InputReaderPolicyTest, Viewports_GetCleared) { static const std::string uniqueId = "local:0"; // We didn't add any viewports yet, so there shouldn't be any. - std::optional<DisplayViewport> internalViewport = - mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); - ASSERT_FALSE(internalViewport); + ASSERT_FALSE(mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL)); // Add an internal viewport, then clear it - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, uniqueId, NO_PORT, ViewportType::INTERNAL); - + DisplayViewport internalViewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, uniqueId, NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(internalViewport); // Check matching by uniqueId - internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId); - ASSERT_TRUE(internalViewport); - ASSERT_EQ(ViewportType::INTERNAL, internalViewport->type); + std::optional<DisplayViewport> receivedInternalViewport = + mFakePolicy->getDisplayViewportByUniqueId(uniqueId); + ASSERT_TRUE(receivedInternalViewport.has_value()); + ASSERT_EQ(internalViewport, *receivedInternalViewport); // Check matching by viewport type - internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); - ASSERT_TRUE(internalViewport); - ASSERT_EQ(uniqueId, internalViewport->uniqueId); + receivedInternalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); + ASSERT_TRUE(receivedInternalViewport.has_value()); + ASSERT_EQ(internalViewport, *receivedInternalViewport); mFakePolicy->clearViewports(); + // Make sure nothing is found after clear - internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId); - ASSERT_FALSE(internalViewport); - internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); - ASSERT_FALSE(internalViewport); + ASSERT_FALSE(mFakePolicy->getDisplayViewportByUniqueId(uniqueId)); + ASSERT_FALSE(mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL)); } TEST_F(InputReaderPolicyTest, Viewports_GetByType) { @@ -420,49 +419,49 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByType) { constexpr ui::LogicalDisplayId virtualDisplayId2 = ui::LogicalDisplayId{3}; // Add an internal viewport - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, internalUniqueId, NO_PORT, - ViewportType::INTERNAL); + DisplayViewport internalViewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, internalUniqueId, NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(internalViewport); // Add an external viewport - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, externalUniqueId, NO_PORT, - ViewportType::EXTERNAL); + DisplayViewport externalViewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, externalUniqueId, NO_PORT, ViewportType::EXTERNAL); + mFakePolicy->addDisplayViewport(externalViewport); // Add an virtual viewport - mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, virtualUniqueId1, NO_PORT, - ViewportType::VIRTUAL); + DisplayViewport virtualViewport1 = + createViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, virtualUniqueId1, NO_PORT, ViewportType::VIRTUAL); + mFakePolicy->addDisplayViewport(virtualViewport1); // Add another virtual viewport - mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, virtualUniqueId2, NO_PORT, - ViewportType::VIRTUAL); + DisplayViewport virtualViewport2 = + createViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, virtualUniqueId2, NO_PORT, ViewportType::VIRTUAL); + mFakePolicy->addDisplayViewport(virtualViewport2); // Check matching by type for internal - std::optional<DisplayViewport> internalViewport = + std::optional<DisplayViewport> receivedInternalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); - ASSERT_TRUE(internalViewport); - ASSERT_EQ(internalUniqueId, internalViewport->uniqueId); + ASSERT_TRUE(receivedInternalViewport.has_value()); + ASSERT_EQ(internalViewport, *receivedInternalViewport); // Check matching by type for external - std::optional<DisplayViewport> externalViewport = + std::optional<DisplayViewport> receivedExternalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::EXTERNAL); - ASSERT_TRUE(externalViewport); - ASSERT_EQ(externalUniqueId, externalViewport->uniqueId); + ASSERT_TRUE(receivedExternalViewport.has_value()); + ASSERT_EQ(externalViewport, *receivedExternalViewport); // Check matching by uniqueId for virtual viewport #1 - std::optional<DisplayViewport> virtualViewport1 = + std::optional<DisplayViewport> receivedVirtualViewport1 = mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1); - ASSERT_TRUE(virtualViewport1); - ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport1->type); - ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId); - ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId); + ASSERT_TRUE(receivedVirtualViewport1.has_value()); + ASSERT_EQ(virtualViewport1, *receivedVirtualViewport1); // Check matching by uniqueId for virtual viewport #2 - std::optional<DisplayViewport> virtualViewport2 = + std::optional<DisplayViewport> receivedVirtualViewport2 = mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2); - ASSERT_TRUE(virtualViewport2); - ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport2->type); - ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId); - ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId); + ASSERT_TRUE(receivedVirtualViewport2.has_value()); + ASSERT_EQ(virtualViewport2, *receivedVirtualViewport2); } @@ -482,24 +481,26 @@ TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) { for (const ViewportType& type : types) { mFakePolicy->clearViewports(); // Add a viewport - mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, uniqueId1, NO_PORT, type); + DisplayViewport viewport1 = + createViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, uniqueId1, NO_PORT, type); + mFakePolicy->addDisplayViewport(viewport1); // Add another viewport - mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, uniqueId2, NO_PORT, type); + DisplayViewport viewport2 = + createViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, uniqueId2, NO_PORT, type); + mFakePolicy->addDisplayViewport(viewport2); // Check that correct display viewport was returned by comparing the display IDs. - std::optional<DisplayViewport> viewport1 = + std::optional<DisplayViewport> receivedViewport1 = mFakePolicy->getDisplayViewportByUniqueId(uniqueId1); - ASSERT_TRUE(viewport1); - ASSERT_EQ(displayId1, viewport1->displayId); - ASSERT_EQ(type, viewport1->type); + ASSERT_TRUE(receivedViewport1.has_value()); + ASSERT_EQ(viewport1, *receivedViewport1); - std::optional<DisplayViewport> viewport2 = + std::optional<DisplayViewport> receivedViewport2 = mFakePolicy->getDisplayViewportByUniqueId(uniqueId2); - ASSERT_TRUE(viewport2); - ASSERT_EQ(displayId2, viewport2->displayId); - ASSERT_EQ(type, viewport2->type); + ASSERT_TRUE(receivedViewport2.has_value()); + ASSERT_EQ(viewport2, *receivedViewport2); // When there are multiple viewports of the same kind, and uniqueId is not specified // in the call to getDisplayViewport, then that situation is not supported. @@ -525,32 +526,27 @@ TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) { // Add the default display first and ensure it gets returned. mFakePolicy->clearViewports(); - mFakePolicy->addDisplayViewport(ui::LogicalDisplayId::DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, uniqueId1, NO_PORT, - ViewportType::INTERNAL); - mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, uniqueId2, NO_PORT, - ViewportType::INTERNAL); - - std::optional<DisplayViewport> viewport = + DisplayViewport viewport1 = createViewport(ui::LogicalDisplayId::DEFAULT, DISPLAY_WIDTH, + DISPLAY_HEIGHT, ui::ROTATION_0, /*isActive=*/true, + uniqueId1, NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(viewport1); + DisplayViewport viewport2 = + createViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, uniqueId2, NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(viewport2); + std::optional<DisplayViewport> receivedViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); - ASSERT_TRUE(viewport); - ASSERT_EQ(ui::LogicalDisplayId::DEFAULT, viewport->displayId); - ASSERT_EQ(ViewportType::INTERNAL, viewport->type); + ASSERT_TRUE(receivedViewport.has_value()); + ASSERT_EQ(viewport1, *receivedViewport); // Add the default display second to make sure order doesn't matter. mFakePolicy->clearViewports(); - mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, uniqueId2, NO_PORT, - ViewportType::INTERNAL); - mFakePolicy->addDisplayViewport(ui::LogicalDisplayId::DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, uniqueId1, NO_PORT, - ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(viewport2); + mFakePolicy->addDisplayViewport(viewport1); - viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); - ASSERT_TRUE(viewport); - ASSERT_EQ(ui::LogicalDisplayId::DEFAULT, viewport->displayId); - ASSERT_EQ(ViewportType::INTERNAL, viewport->type); + receivedViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); + ASSERT_TRUE(receivedViewport.has_value()); + ASSERT_EQ(viewport1, *receivedViewport); } /** @@ -568,28 +564,27 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByPort) { mFakePolicy->clearViewports(); // Add a viewport that's associated with some display port that's not of interest. - mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, uniqueId1, hdmi3, type); + DisplayViewport viewport1 = + createViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, uniqueId1, hdmi3, type); + mFakePolicy->addDisplayViewport(viewport1); // Add another viewport, connected to HDMI1 port - mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, uniqueId2, hdmi1, type); - + DisplayViewport viewport2 = + createViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, uniqueId2, hdmi1, type); + mFakePolicy->addDisplayViewport(viewport2); // Check that correct display viewport was returned by comparing the display ports. std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1); - ASSERT_TRUE(hdmi1Viewport); - ASSERT_EQ(displayId2, hdmi1Viewport->displayId); - ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId); + ASSERT_TRUE(hdmi1Viewport.has_value()); + ASSERT_EQ(viewport2, *hdmi1Viewport); // Check that we can still get the same viewport using the uniqueId hdmi1Viewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId2); - ASSERT_TRUE(hdmi1Viewport); - ASSERT_EQ(displayId2, hdmi1Viewport->displayId); - ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId); - ASSERT_EQ(type, hdmi1Viewport->type); + ASSERT_TRUE(hdmi1Viewport.has_value()); + ASSERT_EQ(viewport2, *hdmi1Viewport); // Check that we cannot find a port with "HDMI2", because we never added one - std::optional<DisplayViewport> hdmi2Viewport = mFakePolicy->getDisplayViewportByPort(hdmi2); - ASSERT_FALSE(hdmi2Viewport); + ASSERT_FALSE(mFakePolicy->getDisplayViewportByPort(hdmi2)); } // --- InputReaderTest --- @@ -1046,11 +1041,14 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Add default and second display. mFakePolicy->clearViewports(); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); - mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, "local:1", hdmi1, - ViewportType::EXTERNAL); + DisplayViewport internalViewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(internalViewport); + DisplayViewport externalViewport = + createViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:1", hdmi1, ViewportType::EXTERNAL); + mFakePolicy->addDisplayViewport(externalViewport); mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::DISPLAY_INFO); mReader->loopOnce(); @@ -1653,7 +1651,7 @@ protected: mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); const auto info = waitForDevice(mDevice->getName()); - ASSERT_TRUE(info); + ASSERT_TRUE(info.has_value()); mDeviceInfo = *info; } @@ -1661,8 +1659,10 @@ protected: ui::Rotation orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort, ViewportType viewportType) { - mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /*isActive=*/true, - uniqueId, physicalPort, viewportType); + DisplayViewport viewport = + createViewport(displayId, width, height, orientation, /*isActive=*/true, uniqueId, + physicalPort, viewportType); + mFakePolicy->addDisplayViewport(viewport); mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::DISPLAY_INFO); } @@ -1721,7 +1721,7 @@ protected: ViewportType::INTERNAL); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); const auto info = waitForDevice(mDevice->getName()); - ASSERT_TRUE(info); + ASSERT_TRUE(info.has_value()); mDeviceInfo = *info; } }; @@ -2053,7 +2053,7 @@ TEST_P(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) { auto externalStylus = createUinputDevice<UinputExternalStylus>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); const auto stylusInfo = waitForDevice(externalStylus->getName()); - ASSERT_TRUE(stylusInfo); + ASSERT_TRUE(stylusInfo.has_value()); // Move mDevice->sendMove(centerPoint + Point(2, 2)); @@ -2122,7 +2122,7 @@ private: mStylus = mStylusDeviceLifecycleTracker.get(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); const auto info = waitForDevice(mStylus->getName()); - ASSERT_TRUE(info); + ASSERT_TRUE(info.has_value()); mStylusInfo = *info; } @@ -2395,7 +2395,7 @@ TEST_F(ExternalStylusIntegrationTest, ExternalStylusConnectionChangesTouchscreen // Connecting an external stylus changes the source of the touchscreen. const auto deviceInfo = waitForDevice(mDevice->getName()); - ASSERT_TRUE(deviceInfo); + ASSERT_TRUE(deviceInfo.has_value()); ASSERT_TRUE(isFromSource(deviceInfo->getSources(), STYLUS_FUSION_SOURCE)); } @@ -2408,7 +2408,7 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) { createUinputDevice<UinputExternalStylusWithPressure>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); const auto stylusInfo = waitForDevice(stylus->getName()); - ASSERT_TRUE(stylusInfo); + ASSERT_TRUE(stylusInfo.has_value()); ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); @@ -2453,7 +2453,7 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { createUinputDevice<UinputExternalStylusWithPressure>(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); const auto stylusInfo = waitForDevice(stylus->getName()); - ASSERT_TRUE(stylusInfo); + ASSERT_TRUE(stylusInfo.has_value()); ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); @@ -2880,9 +2880,10 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { ASSERT_FALSE(mDevice->isEnabled()); // Prepare displays. - mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, UNIQUE_ID, hdmi, - ViewportType::INTERNAL); + DisplayViewport viewport = + createViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, UNIQUE_ID, hdmi, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(viewport); unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), InputReaderConfiguration::Change::DISPLAY_INFO); ASSERT_TRUE(mDevice->isEnabled()); @@ -2917,9 +2918,12 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) { ASSERT_FALSE(mDevice->isEnabled()); // Device should be enabled when a display is found. - mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, - NO_PORT, ViewportType::INTERNAL); + + DisplayViewport secondViewport = + createViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /* isActive= */ true, DISPLAY_UNIQUE_ID, NO_PORT, + ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(secondViewport); unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), InputReaderConfiguration::Change::DISPLAY_INFO); ASSERT_TRUE(mDevice->isEnabled()); @@ -2945,9 +2949,12 @@ TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) { /*changes=*/{}); mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); - mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, - NO_PORT, ViewportType::INTERNAL); + + DisplayViewport secondViewport = + createViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /* isActive= */ true, DISPLAY_UNIQUE_ID, NO_PORT, + ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(secondViewport); const auto initialGeneration = mDevice->getGeneration(); unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), InputReaderConfiguration::Change::DISPLAY_INFO); @@ -7364,35 +7371,39 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIs toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); // down when BTN_TOUCH is pressed, pressure defaults to 1 + processPosition(mapper, 151, 251); processKey(mapper, BTN_TOUCH, 1); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action); + // HOVER_EXIT should have the same coordinates as the previous HOVER_MOVE ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); - + // down at the new position ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0)); + toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0)); // up when BTN_TOUCH is released, hover restored + processPosition(mapper, 152, 252); processKey(mapper, BTN_TOUCH, 0); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + // UP should have the same coordinates as the previous event ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0)); - + toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0)); + // HOVER_ENTER at the new position ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); + toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); + toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0)); // exit hover when pointer goes away processId(mapper, -1); @@ -7400,7 +7411,7 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIs ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); + toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0)); } TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) { @@ -7435,35 +7446,39 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfIts toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); // down when pressure becomes non-zero + processPosition(mapper, 151, 251); processPressure(mapper, RAW_PRESSURE_MAX); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action); + // HOVER_EXIT should have the same coordinates as the previous HOVER_MOVE ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); - + // down at the new position ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0)); + toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0)); // up when pressure becomes 0, hover restored + processPosition(mapper, 152, 252); processPressure(mapper, 0); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + // UP should have the same coordinates as the previous event ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0)); - + toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0)); + // HOVER_ENTER at the new position ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); + toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); + toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0)); // exit hover when pointer goes away processId(mapper, -1); @@ -7471,7 +7486,7 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfIts ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); + toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0)); } /** @@ -7586,8 +7601,10 @@ TEST_F(MultiTouchInputMapperTest, Process_SendsReadTime) { TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) { addConfigurationProperty("touch.deviceType", "touchScreen"); // Don't set touch.enableForInactiveViewport to verify the default behavior. - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + DisplayViewport viewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(viewport); configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); prepareAxes(POSITION); MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); @@ -7606,8 +7623,10 @@ TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) { TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) { addConfigurationProperty("touch.deviceType", "touchScreen"); addConfigurationProperty("touch.enableForInactiveViewport", "1"); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + DisplayViewport viewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(viewport); configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); prepareAxes(POSITION); MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); @@ -7628,8 +7647,10 @@ TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) { TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) { addConfigurationProperty("touch.deviceType", "touchScreen"); addConfigurationProperty("touch.enableForInactiveViewport", "0"); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + DisplayViewport viewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(viewport); std::optional<DisplayViewport> optionalDisplayViewport = mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID); ASSERT_TRUE(optionalDisplayViewport.has_value()); @@ -7684,12 +7705,10 @@ TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) { TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_TouchesNotAborted) { addConfigurationProperty("touch.deviceType", "touchScreen"); addConfigurationProperty("touch.enableForInactiveViewport", "1"); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); - std::optional<DisplayViewport> optionalDisplayViewport = - mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID); - ASSERT_TRUE(optionalDisplayViewport.has_value()); - DisplayViewport displayViewport = *optionalDisplayViewport; + DisplayViewport displayViewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(displayViewport); configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); prepareAxes(POSITION); diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index ac616d0296..dce54728ed 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -68,7 +68,7 @@ public: MOCK_METHOD(InputReaderPolicyInterface*, getPolicy, (), (override)); MOCK_METHOD(EventHubInterface*, getEventHub, (), (override)); - int32_t getNextId() override { return 1; }; + int32_t getNextId() const override { return 1; }; MOCK_METHOD(void, updateLedMetaState, (int32_t metaState), (override)); MOCK_METHOD(int32_t, getLedMetaState, (), (override)); @@ -196,14 +196,14 @@ public: class MockInputDevice : public InputDevice { public: MockInputDevice(InputReaderContext* context, int32_t id, int32_t generation, - const InputDeviceIdentifier& identifier) - : InputDevice(context, id, generation, identifier) {} + const InputDeviceIdentifier& identifier, bool isExternal) + : InputDevice(context, id, generation, identifier), mIsExternal(isExternal) {} MOCK_METHOD(uint32_t, getSources, (), (const, override)); MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const)); MOCK_METHOD(KeyboardType, getKeyboardType, (), (const, override)); MOCK_METHOD(bool, isEnabled, (), ()); - MOCK_METHOD(bool, isExternal, (), (override)); + bool isExternal() override { return mIsExternal; } MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ()); MOCK_METHOD(void, addEmptyEventHubDevice, (int32_t eventHubId), ()); @@ -266,5 +266,6 @@ public: private: int32_t mGeneration = 0; + const bool mIsExternal; }; } // namespace android diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index 1dd32c447b..17acdd49cf 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -1085,10 +1085,9 @@ TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) { class KeyboardInputMapperTest_ExternalAlphabeticDevice : public KeyboardInputMapperUnitTest { protected: void SetUp() override { - InputMapperUnitTest::SetUp(); + InputMapperUnitTest::SetUp(/*bus=*/0, /*isExternal=*/true); ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD)); ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC)); - ON_CALL((*mDevice), isExternal).WillByDefault(Return(true)); EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY | InputDeviceClass::EXTERNAL)); @@ -1102,10 +1101,9 @@ protected: class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public KeyboardInputMapperUnitTest { protected: void SetUp() override { - InputMapperUnitTest::SetUp(); + InputMapperUnitTest::SetUp(/*bus=*/0, /*isExternal=*/true); ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD)); ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::NON_ALPHABETIC)); - ON_CALL((*mDevice), isExternal).WillByDefault(Return(true)); EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::EXTERNAL)); mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index ca0f1e8999..1cfaaa8418 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -57,6 +57,7 @@ void setDefaultInputDeviceInfo(LatencyTracker& tracker) { } const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200); +const auto FIRST_MOUSE_POINTER = PointerBuilder(/*id=*/1, ToolType::MOUSE); /** * This is a convenience method for comparing timelines that also prints the difference between @@ -491,8 +492,13 @@ TEST_F(LatencyTrackerTest, TrackListenerCheck_InputEventActionTypeFieldInputEven /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::BUTTONS}, InputEventActionType::KEY); - InputEventTimeline unknownTimeline( + InputEventTimeline motionScrollTimeline( /*eventTime*/ 12, /*readTime*/ 13, + /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::MOUSE}, + InputEventActionType::MOTION_ACTION_SCROLL); + + InputEventTimeline unknownTimeline( + /*eventTime*/ 14, /*readTime*/ 15, /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN}, InputEventActionType::UNKNOWN_INPUT_EVENT); @@ -529,8 +535,15 @@ TEST_F(LatencyTrackerTest, TrackListenerCheck_InputEventActionTypeFieldInputEven .readTime(keyUpTimeline.readTime) .deviceId(DEVICE_ID) .build()); + mTracker->trackListener( + MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL, AINPUT_SOURCE_MOUSE, inputEventId + 5) + .eventTime(motionScrollTimeline.eventTime) + .readTime(motionScrollTimeline.readTime) + .deviceId(DEVICE_ID) + .pointer(FIRST_MOUSE_POINTER) + .build()); mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN, - AINPUT_SOURCE_TOUCHSCREEN, inputEventId + 5) + AINPUT_SOURCE_TOUCHSCREEN, inputEventId + 6) .eventTime(unknownTimeline.eventTime) .readTime(unknownTimeline.readTime) .deviceId(DEVICE_ID) @@ -541,7 +554,8 @@ TEST_F(LatencyTrackerTest, TrackListenerCheck_InputEventActionTypeFieldInputEven std::vector<InputEventTimeline> expectedTimelines = {motionDownTimeline, motionMoveTimeline, motionUpTimeline, keyDownTimeline, - keyUpTimeline, unknownTimeline}; + keyUpTimeline, motionScrollTimeline, + unknownTimeline}; assertReceivedTimelines(expectedTimelines); } diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp index d15048dddf..b7cb348236 100644 --- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp +++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp @@ -53,8 +53,9 @@ static constexpr int32_t ACTION_POINTER_1_DOWN = */ class MultiTouchInputMapperUnitTest : public InputMapperUnitTest { protected: - void SetUp() override { - InputMapperUnitTest::SetUp(); + void SetUp() override { SetUp(/*bus=*/0, /*isExternal=*/false); } + void SetUp(int bus, bool isExternal) override { + InputMapperUnitTest::SetUp(bus, isExternal); // Present scan codes expectScanCodes(/*present=*/true, @@ -108,9 +109,10 @@ protected: mockSlotValues({}); mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, "local:0", NO_PORT, - ViewportType::INTERNAL); + DisplayViewport internalViewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(internalViewport); mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext, mFakePolicy->getReaderConfiguration()); } diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp index 157bee33e1..548df2255b 100644 --- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp +++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp @@ -89,9 +89,9 @@ namespace vd_flags = android::companion::virtualdevice::flags; */ class RotaryEncoderInputMapperTest : public InputMapperUnitTest { protected: - void SetUp() override { SetUpWithBus(BUS_USB); } - void SetUpWithBus(int bus) override { - InputMapperUnitTest::SetUpWithBus(bus); + void SetUp() override { SetUp(/*bus=*/0, /*isExternal=*/false); } + void SetUp(int bus, bool isExternal) override { + InputMapperUnitTest::SetUp(bus, isExternal); EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL)) .WillRepeatedly(Return(true)); diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp index ea69fffeaa..5f5aa63704 100644 --- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp +++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp @@ -18,10 +18,13 @@ #include <android-base/logging.h> #include <gtest/gtest.h> +#include <input/AccelerationCurve.h> +#include <log/log.h> #include <thread> #include "InputMapperTest.h" #include "InterfaceMocks.h" +#include "TestConstants.h" #include "TestEventMatchers.h" #define TAG "TouchpadInputMapper_test" @@ -121,9 +124,10 @@ protected: */ TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); - + DisplayViewport viewport = + createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(viewport); std::list<NotifyArgs> args; args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration, @@ -190,4 +194,67 @@ TEST_F(TouchpadInputMapperTest, TouchpadHardwareState) { mFakePolicy->assertTouchpadHardwareStateNotified(); } +TEST_F(TouchpadInputMapperTest, TouchpadAccelerationDisabled) { + mReaderConfiguration.touchpadAccelerationEnabled = false; + mReaderConfiguration.touchpadPointerSpeed = 3; + + std::list<NotifyArgs> args = + mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::TOUCHPAD_SETTINGS); + auto* touchpadMapper = static_cast<TouchpadInputMapper*>(mMapper.get()); + + const auto accelCurvePropsDisabled = + touchpadMapper->getGesturePropertyForTesting("Pointer Accel Curve"); + ASSERT_TRUE(accelCurvePropsDisabled.has_value()); + std::vector<double> curveValuesDisabled = accelCurvePropsDisabled.value().getRealValues(); + std::vector<AccelerationCurveSegment> curve = + createFlatAccelerationCurve(mReaderConfiguration.touchpadPointerSpeed); + double expectedBaseGain = curve[0].baseGain; + ASSERT_EQ(curveValuesDisabled[0], std::numeric_limits<double>::infinity()); + ASSERT_EQ(curveValuesDisabled[1], 0); + ASSERT_NEAR(curveValuesDisabled[2], expectedBaseGain, EPSILON); + ASSERT_EQ(curveValuesDisabled[3], 0); +} + +TEST_F(TouchpadInputMapperTest, TouchpadAccelerationEnabled) { + // Enable touchpad acceleration. + mReaderConfiguration.touchpadAccelerationEnabled = true; + mReaderConfiguration.touchpadPointerSpeed = 3; + + std::list<NotifyArgs> args = + mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::TOUCHPAD_SETTINGS); + ASSERT_THAT(args, testing::IsEmpty()); + + auto* touchpadMapper = static_cast<TouchpadInputMapper*>(mMapper.get()); + + // Get the acceleration curve properties when acceleration is enabled. + const auto accelCurvePropsEnabled = + touchpadMapper->getGesturePropertyForTesting("Pointer Accel Curve"); + ASSERT_TRUE(accelCurvePropsEnabled.has_value()); + + // Get the curve values. + std::vector<double> curveValuesEnabled = accelCurvePropsEnabled.value().getRealValues(); + + // Use createAccelerationCurveForPointerSensitivity to get expected curve segments. + std::vector<AccelerationCurveSegment> expectedCurveSegments = + createAccelerationCurveForPointerSensitivity(mReaderConfiguration.touchpadPointerSpeed); + + // Iterate through the segments and compare the values. + for (size_t i = 0; i < expectedCurveSegments.size(); ++i) { + // Check max speed. + if (std::isinf(expectedCurveSegments[i].maxPointerSpeedMmPerS)) { + ASSERT_TRUE(std::isinf(curveValuesEnabled[i * 4 + 0])); + } else { + ASSERT_NEAR(curveValuesEnabled[i * 4 + 0], + expectedCurveSegments[i].maxPointerSpeedMmPerS, EPSILON); + } + + // Check that the x^2 term is zero. + ASSERT_NEAR(curveValuesEnabled[i * 4 + 1], 0, EPSILON); + ASSERT_NEAR(curveValuesEnabled[i * 4 + 2], expectedCurveSegments[i].baseGain, EPSILON); + ASSERT_NEAR(curveValuesEnabled[i * 4 + 3], expectedCurveSegments[i].reciprocal, EPSILON); + } +} + } // namespace android diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index a1da39ae14..846260a946 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -346,7 +346,7 @@ public: } InputReaderPolicyInterface* getPolicy() override { return mPolicy.get(); } EventHubInterface* getEventHub() override { return mEventHub.get(); } - int32_t getNextId() override { return mFdp->ConsumeIntegral<int32_t>(); } + int32_t getNextId() const override { return mFdp->ConsumeIntegral<int32_t>(); } void updateLedMetaState(int32_t metaState) override{}; int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); }; diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS index 7347ac74e6..3ccdc17061 100644 --- a/services/sensorservice/OWNERS +++ b/services/sensorservice/OWNERS @@ -1 +1,3 @@ bduddie@google.com +arthuri@google.com +rockyfang@google.com diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 05f7d1ba21..ea7d6d7602 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -26,15 +26,15 @@ cc_aconfig_library { cc_defaults { name: "surfaceflinger_defaults", cflags: [ + "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", "-Wall", + "-Wconversion", "-Werror", "-Wextra", "-Wformat", "-Wthread-safety", - "-Wunused", "-Wunreachable-code", - "-Wconversion", - "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", + "-Wunused", ], } @@ -43,39 +43,41 @@ cc_defaults { defaults: [ "android.hardware.graphics.composer3-ndk_shared", "librenderengine_deps", - "libtimestats_deps", "libsurfaceflinger_common_deps", - "surfaceflinger_defaults", "libsurfaceflinger_proto_deps", + "libtimestats_deps", "poweradvisor_deps", + "surfaceflinger_defaults", ], cflags: [ - "-DLOG_TAG=\"SurfaceFlinger\"", - "-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", + "-DGL_GLEXT_PROTOTYPES", + "-DLOG_TAG=\"SurfaceFlinger\"", ], shared_libs: [ + "android.hardware.common-V2-ndk", + "android.hardware.common.fmq-V1-ndk", "android.hardware.configstore-utils", "android.hardware.configstore@1.0", "android.hardware.configstore@1.1", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.allocator@3.0", "android.hardware.graphics.common@1.2", - "android.hardware.common-V2-ndk", - "android.hardware.common.fmq-V1-ndk", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", "android.os.flags-aconfig-cc-host", + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libSurfaceFlingerProp", + "libaconfig_storage_read_api_cc", "libbase", "libbinder", "libbinder_ndk", "libcutils", - "libEGL", "libfmq", - "libGLESv1_CM", - "libGLESv2", "libgui", "libhidlbase", "liblog", @@ -86,8 +88,6 @@ cc_defaults { "libsync", "libui", "libutils", - "libSurfaceFlingerProp", - "libaconfig_storage_read_api_cc", ], static_libs: [ "iinputflinger_aidl_lib_static", @@ -105,11 +105,11 @@ cc_defaults { "libtonemap", ], header_libs: [ + "android.hardware.graphics.composer3-command-buffer", "android.hardware.graphics.composer@2.1-command-buffer", "android.hardware.graphics.composer@2.2-command-buffer", "android.hardware.graphics.composer@2.3-command-buffer", "android.hardware.graphics.composer@2.4-command-buffer", - "android.hardware.graphics.composer3-command-buffer", ], export_static_lib_headers: [ "libcompositionengine", @@ -125,8 +125,8 @@ cc_defaults { "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", - "libpowermanager", "libhidlbase", + "libpowermanager", ], // TODO (marissaw): this library is not used by surfaceflinger. This is here so // the library compiled in a way that is accessible to system partition when running @@ -177,7 +177,6 @@ cc_library_headers { filegroup { name: "libsurfaceflinger_backend_sources", srcs: [ - "PowerAdvisor/*.cpp", "DisplayHardware/AidlComposerHal.cpp", "DisplayHardware/ComposerHal.cpp", "DisplayHardware/FramebufferSurface.cpp", @@ -185,6 +184,7 @@ filegroup { "DisplayHardware/HWComposer.cpp", "DisplayHardware/HidlComposerHal.cpp", "DisplayHardware/VirtualDisplaySurface.cpp", + "PowerAdvisor/*.cpp", ], } @@ -208,21 +208,20 @@ filegroup { "DisplayDevice.cpp", "DisplayRenderArea.cpp", "Effects/Daltonizer.cpp", + "FpsReporter.cpp", + "FrameTracer/FrameTracer.cpp", + "FrameTracker.cpp", "FrontEnd/LayerCreationArgs.cpp", "FrontEnd/LayerHandle.cpp", - "FrontEnd/LayerSnapshot.cpp", - "FrontEnd/LayerSnapshotBuilder.cpp", "FrontEnd/LayerHierarchy.cpp", "FrontEnd/LayerLifecycleManager.cpp", + "FrontEnd/LayerSnapshot.cpp", + "FrontEnd/LayerSnapshotBuilder.cpp", "FrontEnd/RequestedLayerState.cpp", "FrontEnd/TransactionHandler.cpp", - "FpsReporter.cpp", - "FrameTracer/FrameTracer.cpp", - "FrameTracker.cpp", "HdrLayerInfoReporter.cpp", "HdrSdrRatioOverlay.cpp", "Jank/JankTracker.cpp", - "WindowInfosListenerInvoker.cpp", "Layer.cpp", "LayerFE.cpp", "LayerProtoHelper.cpp", @@ -234,10 +233,10 @@ filegroup { "RenderArea.cpp", "Scheduler/EventThread.cpp", "Scheduler/FrameRateOverrideMappings.cpp", - "Scheduler/OneShotTimer.cpp", "Scheduler/LayerHistory.cpp", "Scheduler/LayerInfo.cpp", "Scheduler/MessageQueue.cpp", + "Scheduler/OneShotTimer.cpp", "Scheduler/RefreshRateSelector.cpp", "Scheduler/Scheduler.cpp", "Scheduler/SmallAreaDetectionAllowMappings.cpp", @@ -253,19 +252,20 @@ filegroup { "Tracing/LayerDataSource.cpp", "Tracing/LayerTracing.cpp", "Tracing/TransactionDataSource.cpp", - "Tracing/TransactionTracing.cpp", "Tracing/TransactionProtoParser.cpp", + "Tracing/TransactionTracing.cpp", "Tracing/tools/LayerTraceGenerator.cpp", "TransactionCallbackInvoker.cpp", "TunnelModeEnabledReporter.cpp", + "WindowInfosListenerInvoker.cpp", ], } cc_defaults { name: "libsurfaceflinger_binary", defaults: [ - "surfaceflinger_defaults", "libsurfaceflinger_production_defaults", + "surfaceflinger_defaults", ], cflags: [ "-DLOG_TAG=\"SurfaceFlinger\"", @@ -331,9 +331,9 @@ cc_library_shared { "android.hardware.configstore@1.1", "android.hardware.graphics.common@1.2", "libhidlbase", + "liblog", "libui", "libutils", - "liblog", ], static_libs: [ "libSurfaceFlingerProperties", @@ -354,10 +354,10 @@ cc_library { generated_headers: ["statslog_surfaceflinger.h"], export_generated_headers: ["statslog_surfaceflinger.h"], shared_libs: [ + "android.os.statsbootstrap_aidl-cpp", "libbinder", "libstatsbootstrap", "libutils", - "android.os.statsbootstrap_aidl-cpp", ], } diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 8529c72419..bb6bebed8d 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -26,12 +26,15 @@ #include <android/binder_manager.h> #include <common/FlagManager.h> #include <common/trace.h> +#include <fmt/core.h> #include <log/log.h> #include <aidl/android/hardware/graphics/composer3/BnComposerCallback.h> #include <algorithm> #include <cinttypes> +#include <string> +#include <string_view> #include "HWC2.h" @@ -229,25 +232,32 @@ private: HWC2::ComposerCallback& mCallback; }; -std::string AidlComposer::instance(const std::string& serviceName) { - return std::string(AidlIComposer::descriptor) + "/" + serviceName; +std::string AidlComposer::ensureFullyQualifiedName(std::string_view serviceName) { + if (!serviceName.starts_with(AidlIComposer::descriptor)) { + return fmt::format("{}/{}", AidlIComposer::descriptor, serviceName); + } else { + return std::string{serviceName}; + } } -bool AidlComposer::isDeclared(const std::string& serviceName) { - return AServiceManager_isDeclared(instance(serviceName).c_str()); +bool AidlComposer::namesAnAidlComposerService(std::string_view serviceName) { + if (!serviceName.starts_with(AidlIComposer::descriptor)) { + return AServiceManager_isDeclared(ensureFullyQualifiedName(serviceName).c_str()); + } + return true; } AidlComposer::AidlComposer(const std::string& serviceName) { // This only waits if the service is actually declared - mAidlComposer = AidlIComposer::fromBinder( - ndk::SpAIBinder(AServiceManager_waitForService(instance(serviceName).c_str()))); + mAidlComposer = AidlIComposer::fromBinder(ndk::SpAIBinder( + AServiceManager_waitForService(ensureFullyQualifiedName(serviceName).c_str()))); if (!mAidlComposer) { LOG_ALWAYS_FATAL("Failed to get AIDL composer service"); return; } if (!mAidlComposer->createClient(&mAidlComposerClient).isOk()) { - LOG_ALWAYS_FATAL("Can't create AidlComposerClient, fallback to HIDL"); + LOG_ALWAYS_FATAL("Can't create AidlComposerClient"); return; } @@ -347,7 +357,9 @@ void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback); ndk::SpAIBinder binder = mAidlComposerCallback->asBinder(); - AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2); + if (!FlagManager::getInstance().disable_sched_fifo_composer_callback()) { + AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2); + } const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback); if (!status.isOk()) { @@ -686,6 +698,36 @@ Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLay return error; } +Error AidlComposer::getLayerPresentFences(Display display, std::vector<Layer>* outLayers, + std::vector<int>* outFences, + std::vector<int64_t>* outLatenciesNanos) { + Error error = Error::NONE; + std::vector<PresentFence::LayerPresentFence> fences; + { + mMutex.lock_shared(); + if (auto reader = getReader(display)) { + fences = reader->get().takeLayerPresentFences(translate<int64_t>(display)); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + } + + outLayers->reserve(fences.size()); + outFences->reserve(fences.size()); + outLatenciesNanos->reserve(fences.size()); + + for (auto& fence : fences) { + outLayers->emplace_back(translate<Layer>(fence.layer)); + // take ownership + const int fenceOwner = fence.bufferFence.get(); + *fence.bufferFence.getR() = -1; + outFences->emplace_back(fenceOwner); + outLatenciesNanos->emplace_back(fence.bufferLatencyNanos); + } + return error; +} + Error AidlComposer::presentDisplay(Display display, int* outPresentFence) { const auto displayId = translate<int64_t>(display); SFTRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 82006f407b..5fcc8b00ba 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -24,7 +24,7 @@ #include <functional> #include <optional> #include <string> -#include <utility> +#include <string_view> #include <vector> #include <android/hardware/graphics/composer/2.4/IComposer.h> @@ -53,7 +53,8 @@ class AidlIComposerCallbackWrapper; // Composer is a wrapper to IComposer, a proxy to server-side composer. class AidlComposer final : public Hwc2::Composer { public: - static bool isDeclared(const std::string& serviceName); + // Returns true if serviceName appears to be something that is meant to be used by AidlComposer. + static bool namesAnAidlComposerService(std::string_view serviceName); explicit AidlComposer(const std::string& serviceName); ~AidlComposer() override; @@ -106,6 +107,10 @@ public: Error getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) override; + Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers, + std::vector<int>* outFences, + std::vector<int64_t>* outLatenciesNanos) override; + Error presentDisplay(Display display, int* outPresentFence) override; Error setActiveConfig(Display display, Config config) override; @@ -254,8 +259,8 @@ private: // this function to execute the command queue. Error execute(Display) REQUIRES_SHARED(mMutex); - // returns the default instance name for the given service - static std::string instance(const std::string& serviceName); + // Ensures serviceName is fully qualified. + static std::string ensureFullyQualifiedName(std::string_view serviceName); ftl::Optional<std::reference_wrapper<ComposerClientWriter>> getWriter(Display) REQUIRES_SHARED(mMutex); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index d69a923b4e..1e4132cfc6 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -26,7 +26,7 @@ namespace android::Hwc2 { Composer::~Composer() = default; std::unique_ptr<Composer> Composer::create(const std::string& serviceName) { - if (AidlComposer::isDeclared(serviceName)) { + if (AidlComposer::namesAnAidlComposerService(serviceName)) { return std::make_unique<AidlComposer>(serviceName); } diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 6e431bb878..018ee6e461 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -157,6 +157,10 @@ public: virtual Error getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) = 0; + virtual Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers, + std::vector<int>* outFences, + std::vector<int64_t>* outLatenciesNanos) = 0; + virtual Error presentDisplay(Display display, int* outPresentFence) = 0; virtual Error setActiveConfig(Display display, Config config) = 0; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index e63a14bb5e..252c6b692d 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -640,7 +640,15 @@ Error Display::getRequestedLuts(LayerLuts* outLuts, lutFileDescriptorMapper.emplace_or_replace(layer.get(), ndk::ScopedFileDescriptor( layerLut.luts.pfd.release())); + } else { + ALOGE("getRequestedLuts: invalid luts on layer %" PRIu64 " found" + " on display %" PRIu64 ". pfd.get()=%d, offsets.has_value()=%d", + layerIds[i], mId, layerLut.luts.pfd.get(), layerLut.luts.offsets.has_value()); } + } else { + ALOGE("getRequestedLuts: invalid layer %" PRIu64 " found" + " on display %" PRIu64, + layerIds[i], mId); } } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 7f94428617..c47943f6c2 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -138,13 +138,13 @@ bool HWComposer::hasDisplayCapability(HalDisplayId displayId, DisplayCapability } std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId, - hal::Connection connection) { - switch (connection) { - case hal::Connection::CONNECTED: + HotplugEvent event) { + switch (event) { + case HotplugEvent::Connected: return onHotplugConnect(hwcDisplayId); - case hal::Connection::DISCONNECTED: + case HotplugEvent::Disconnected: return onHotplugDisconnect(hwcDisplayId); - case hal::Connection::INVALID: + case HotplugEvent::LinkUnstable: return {}; } } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index b1b997a197..d60f6ffb7c 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -231,11 +231,12 @@ public: // Events handling --------------------------------------------------------- - // Returns stable display ID (and display name on connection of new or previously disconnected - // display), or std::nullopt if hotplug event was ignored. + enum class HotplugEvent { Connected, Disconnected, LinkUnstable }; + + // Returns the stable display ID of the display for which the hotplug event was received, or + // std::nullopt if hotplug event was ignored. // This function is called from SurfaceFlinger. - virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, - hal::Connection) = 0; + virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, HotplugEvent) = 0; // If true we'll update the DeviceProductInfo on subsequent hotplug connected events. // TODO(b/157555476): Remove when the framework has proper support for headless mode @@ -435,9 +436,7 @@ public: // Events handling --------------------------------------------------------- - // Returns PhysicalDisplayId (and display name on connection of new or previously disconnected - // display), or std::nullopt if hotplug event was ignored. - std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override; + std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, HotplugEvent) override; bool updatesDeviceProductInfoOnHotplugReconnect() const override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index ec155392eb..a010353423 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -28,6 +28,7 @@ #include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h> #include <android/binder_manager.h> #include <android/hardware/graphics/composer/2.1/types.h> +#include <common/FlagManager.h> #include <common/trace.h> #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> #include <hidl/HidlTransportSupport.h> @@ -301,7 +302,9 @@ std::string HidlComposer::dumpDebugInfo() { } void HidlComposer::registerCallback(const sp<IComposerCallback>& callback) { - android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2); + if (!FlagManager::getInstance().disable_sched_fifo_composer_callback()) { + android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2); + } auto ret = [&]() { if (mClient_2_4) { @@ -590,6 +593,11 @@ Error HidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLay return Error::NONE; } +Error HidlComposer::getLayerPresentFences(Display, std::vector<Layer>*, std::vector<int>*, + std::vector<int64_t>*) { + return Error::UNSUPPORTED; +} + Error HidlComposer::presentDisplay(Display display, int* outPresentFence) { SFTRACE_NAME("HwcPresentDisplay"); mWriter.selectDisplay(display); diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index cacdb8ce52..86ca4b1782 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -214,6 +214,10 @@ public: Error getReleaseFences(Display display, std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences) override; + Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers, + std::vector<int>* outFences, + std::vector<int64_t>* outLatenciesNanos) override; + Error presentDisplay(Display display, int* outPresentFence) override; Error setActiveConfig(Display display, Config config) override; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 86d7388f5b..fece3122a0 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -611,7 +611,11 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish; } - if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { + const nsecs_t presentThreshold = + FlagManager::getInstance().increase_missed_frame_jank_threshold() + ? mJankClassificationThresholds.presentThresholdExtended + : mJankClassificationThresholds.presentThresholdLegacy; + if (std::abs(presentDelta) > presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; // Jank that is missing by less than the render rate period is classified as partial jank, @@ -629,9 +633,8 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r } else if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) { if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) { // Finish on time, Present early - if (deltaToVsync < mJankClassificationThresholds.presentThreshold || - deltaToVsync >= refreshRate.getPeriodNsecs() - - mJankClassificationThresholds.presentThreshold) { + if (deltaToVsync < presentThreshold || + deltaToVsync >= refreshRate.getPeriodNsecs() - presentThreshold) { // Delta factor of vsync mJankType = JankType::SurfaceFlingerScheduling; } else { @@ -667,9 +670,8 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r if (!(mJankType & JankType::BufferStuffing)) { // In a stuffed state, if the app finishes on time and there is no display frame // jank, only buffer stuffing is the root cause of the jank. - if (deltaToVsync < mJankClassificationThresholds.presentThreshold || - deltaToVsync >= refreshRate.getPeriodNsecs() - - mJankClassificationThresholds.presentThreshold) { + if (deltaToVsync < presentThreshold || + deltaToVsync >= refreshRate.getPeriodNsecs() - presentThreshold) { // Delta factor of vsync mJankType |= JankType::SurfaceFlingerScheduling; } else { @@ -1091,7 +1093,11 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& ? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs() : 0; - if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { + nsecs_t presentThreshold = FlagManager::getInstance().increase_missed_frame_jank_threshold() + ? mJankClassificationThresholds.presentThresholdExtended + : mJankClassificationThresholds.presentThresholdLegacy; + + if (std::abs(presentDelta) > presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; // Jank that is missing by less than the render rate period is classified as partial jank, @@ -1122,9 +1128,8 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) { if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) { // Finish on time, Present early - if (deltaToVsync < mJankClassificationThresholds.presentThreshold || - deltaToVsync >= (mRefreshRate.getPeriodNsecs() - - mJankClassificationThresholds.presentThreshold)) { + if (deltaToVsync < presentThreshold || + deltaToVsync >= (mRefreshRate.getPeriodNsecs() - presentThreshold)) { // Delta is a factor of vsync if its within the presentTheshold on either side // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms. @@ -1142,7 +1147,7 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& } } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) { if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <= - mJankClassificationThresholds.presentThreshold || + presentThreshold || previousPresentTime > mSurfaceFlingerPredictions.presentTime) { // The previous frame was either presented in the current frame's expected vsync or // it was presented even later than the current frame's expected vsync. @@ -1151,9 +1156,8 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish && !(mJankType & JankType::SurfaceFlingerStuffing)) { // Finish on time, Present late - if (deltaToVsync < mJankClassificationThresholds.presentThreshold || - deltaToVsync >= (mRefreshRate.getPeriodNsecs() - - mJankClassificationThresholds.presentThreshold)) { + if (deltaToVsync < presentThreshold || + deltaToVsync >= (mRefreshRate.getPeriodNsecs() - presentThreshold)) { // Delta is a factor of vsync if its within the presentTheshold on either side // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms. @@ -1165,8 +1169,7 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) { if (!(mJankType & JankType::SurfaceFlingerStuffing) || mSurfaceFlingerActuals.presentTime - previousPresentTime > - mRefreshRate.getPeriodNsecs() + - mJankClassificationThresholds.presentThreshold) { + mRefreshRate.getPeriodNsecs() + presentThreshold) { // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame // was presented more than a vsync late. if (mGpuFence != FenceTime::NO_FENCE) { diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index a47bd573de..9fedb57aca 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -107,7 +107,10 @@ struct TimelineItem { struct JankClassificationThresholds { // The various thresholds for App and SF. If the actual timestamp falls within the threshold // compared to prediction, we treat it as on time. - nsecs_t presentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); + nsecs_t presentThresholdLegacy = + std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); + nsecs_t presentThresholdExtended = + std::chrono::duration_cast<std::chrono::nanoseconds>(4ms).count(); nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(0ms).count(); nsecs_t startThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); }; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 7289e2f201..86ef6ca61e 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -264,28 +264,21 @@ void updateVisibility(LayerSnapshot& snapshot, bool visible) { } snapshot.isVisible = visible; - if (FlagManager::getInstance().skip_invisible_windows_in_input()) { - const bool visibleForInput = - snapshot.isVisible || (snapshot.hasInputInfo() && !snapshot.isHiddenByPolicy()); - snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, - !visibleForInput); - } else { - // TODO(b/238781169) we are ignoring this compat for now, since we will have - // to remove any optimization based on visibility. - - // For compatibility reasons we let layers which can receive input - // receive input before they have actually submitted a buffer. Because - // of this we use canReceiveInput instead of isVisible to check the - // policy-visibility, ignoring the buffer state. However for layers with - // hasInputInfo()==false we can use the real visibility state. - // We are just using these layers for occlusion detection in - // InputDispatcher, and obviously if they aren't visible they can't occlude - // anything. - const bool visibleForInput = - snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible; - snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, - !visibleForInput); - } + // TODO(b/238781169) we are ignoring this compat for now, since we will have + // to remove any optimization based on visibility. + + // For compatibility reasons we let layers which can receive input + // receive input before they have actually submitted a buffer. Because + // of this we use canReceiveInput instead of isVisible to check the + // policy-visibility, ignoring the buffer state. However for layers with + // hasInputInfo()==false we can use the real visibility state. + // We are just using these layers for occlusion detection in + // InputDispatcher, and obviously if they aren't visible they can't occlude + // anything. + const bool visibleForInput = + snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible; + snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput); + LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false", snapshot.getDebugString().c_str()); } @@ -1185,7 +1178,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, auto displayInfo = displayInfoOpt.value_or(sDefaultInfo); if (!requested.hasInputInfo()) { - snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL; + snapshot.inputInfo.inputConfig |= InputConfig::NO_INPUT_CHANNEL; } fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot); diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 591ebb2043..1d53e719ab 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -573,7 +573,7 @@ bool RequestedLayerState::needsInputInfo() const { return false; } - if ((sidebandStream != nullptr) || (externalTexture != nullptr)) { + if (hasBufferOrSidebandStream() || fillsColor()) { return true; } @@ -586,6 +586,15 @@ bool RequestedLayerState::needsInputInfo() const { windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); } +bool RequestedLayerState::hasBufferOrSidebandStream() const { + return ((sidebandStream != nullptr) || (externalTexture != nullptr)); +} + +bool RequestedLayerState::fillsColor() const { + return !hasBufferOrSidebandStream() && color.r >= 0.0_hf && color.g >= 0.0_hf && + color.b >= 0.0_hf; +} + bool RequestedLayerState::hasBlur() const { return backgroundBlurRadius > 0 || blurRegions.size() > 0; } diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index dd861a74f4..723237940d 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -88,6 +88,8 @@ struct RequestedLayerState : layer_state_t { bool hasValidRelativeParent() const; bool hasInputInfo() const; bool needsInputInfo() const; + bool hasBufferOrSidebandStream() const; + bool fillsColor() const; bool hasBlur() const; bool hasFrameUpdate() const; bool hasReadyFrame() const; diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index c6d7160818..0efc396bcb 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -344,7 +344,8 @@ sp<EventThreadConnection> EventThread::createEventConnection( auto connection = sp<EventThreadConnection>::make(const_cast<EventThread*>(this), IPCThreadState::self()->getCallingUid(), eventRegistration); - if (FlagManager::getInstance().misc1()) { + if (FlagManager::getInstance().misc1() && + !FlagManager::getInstance().disable_sched_fifo_sf_sched()) { const int policy = SCHED_FIFO; connection->setMinSchedulerPolicy(policy, sched_get_priority_min(policy)); } diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp index 20c58eb52f..6a5eeba823 100644 --- a/services/surfaceflinger/Scheduler/src/Timer.cpp +++ b/services/surfaceflinger/Scheduler/src/Timer.cpp @@ -24,6 +24,7 @@ #include <sys/timerfd.h> #include <sys/unistd.h> +#include <common/FlagManager.h> #include <common/trace.h> #include <ftl/concat.h> #include <ftl/enum.h> @@ -155,8 +156,10 @@ bool Timer::dispatch() { setDebugState(DebugState::Running); struct sched_param param = {0}; param.sched_priority = 2; - if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) { - ALOGW("Failed to set SCHED_FIFO on dispatch thread"); + if (!FlagManager::getInstance().disable_sched_fifo_sf_sched()) { + if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) { + ALOGW("Failed to set SCHED_FIFO on dispatch thread"); + } } if (pthread_setname_np(pthread_self(), "TimerDispatch") != 0) { diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1f8557c675..d117753b4b 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -135,7 +135,6 @@ #include "DisplayDevice.h" #include "DisplayHardware/ComposerHal.h" #include "DisplayHardware/FramebufferSurface.h" -#include "DisplayHardware/HWComposer.h" #include "DisplayHardware/Hal.h" #include "DisplayHardware/VirtualDisplaySurface.h" #include "DisplayRenderArea.h" @@ -1071,7 +1070,8 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { void SurfaceFlinger::initBootProperties() { property_set("service.sf.present_timestamp", mHasReliablePresentFences ? "1" : "0"); - if (base::GetBoolProperty("debug.sf.boot_animation"s, true)) { + if (base::GetBoolProperty("debug.sf.boot_animation"s, true) && + (base::GetIntProperty("debug.sf.nobootanimation"s, 0) == 0)) { // Reset and (if needed) start BootAnimation. property_set("service.bootanim.exit", "0"); property_set("service.bootanim.progress", "0"); @@ -2292,12 +2292,12 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, DisplayHotplugEvent event) { if (event == DisplayHotplugEvent::CONNECTED || event == DisplayHotplugEvent::DISCONNECTED) { - hal::Connection connection = (event == DisplayHotplugEvent::CONNECTED) - ? hal::Connection::CONNECTED - : hal::Connection::DISCONNECTED; + const HWComposer::HotplugEvent hotplugEvent = event == DisplayHotplugEvent::CONNECTED + ? HWComposer::HotplugEvent::Connected + : HWComposer::HotplugEvent::Disconnected; { std::lock_guard<std::mutex> lock(mHotplugMutex); - mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection}); + mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, hotplugEvent}); } if (mScheduler) { @@ -3271,12 +3271,12 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const auto schedule = mScheduler->getVsyncSchedule(); const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime); - const Period vsyncPeriod = schedule->period(); + const Fps renderRate = pacesetterDisplay->refreshRateSelector().getActiveMode().fps; const nsecs_t vsyncPhase = mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset; - const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase, - presentLatency.ns()); + const CompositorTiming compositorTiming(vsyncDeadline.ns(), renderRate.getPeriodNsecs(), + vsyncPhase, presentLatency.ns()); ui::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay; { @@ -3658,13 +3658,13 @@ bool SurfaceFlinger::configureLocked() { events = std::move(mPendingHotplugEvents); } - for (const auto [hwcDisplayId, connection] : events) { - if (auto info = getHwComposer().onHotplug(hwcDisplayId, connection)) { + for (const auto [hwcDisplayId, event] : events) { + if (auto info = getHwComposer().onHotplug(hwcDisplayId, event)) { const auto displayId = info->id; const ftl::Concat displayString("display ", displayId.value, "(HAL ID ", hwcDisplayId, ')'); - if (connection == hal::Connection::CONNECTED) { + if (event == HWComposer::HotplugEvent::Connected) { const auto activeModeIdOpt = processHotplugConnect(displayId, hwcDisplayId, std::move(*info), displayString.c_str()); @@ -3975,9 +3975,6 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, displaySurface, producer); if (mScheduler && !display->isVirtual()) { - // TODO(b/241285876): Annotate `processDisplayAdded` instead. - ftl::FakeGuard guard(kMainThreadContext); - // For hotplug reconnect, renew the registration since display modes have been reloaded. mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector(), mActiveDisplayId); @@ -7116,7 +7113,7 @@ status_t SurfaceFlinger::setSchedFifo(bool enabled) { struct sched_param param = {0}; int sched_policy; - if (enabled) { + if (enabled && !FlagManager::getInstance().disable_sched_fifo_sf()) { sched_policy = SCHED_FIFO; param.sched_priority = kFifoPriority; } else { @@ -7648,7 +7645,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( if (hdrBuffer && gainmapBuffer) { ftl::SharedFuture<FenceResult> hdrRenderFuture = - renderScreenImpl(renderArea.get(), hdrBuffer, regionSampling, grayscale, + renderScreenImpl(std::move(renderArea), hdrBuffer, regionSampling, grayscale, isProtected, captureResults, displayState, layers); captureResults.buffer = buffer->getBuffer(); captureResults.optionalGainMap = gainmapBuffer->getBuffer(); @@ -7672,7 +7669,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( }) .share(); } else { - renderFuture = renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, + renderFuture = renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected, captureResults, displayState, layers); } @@ -7693,7 +7690,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( } ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( - const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer, + std::unique_ptr<const RenderArea> renderArea, + const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults, const std::optional<OutputCompositionState>& displayState, const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { @@ -7906,9 +7904,8 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy(); ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str()); - if (const bool isPacesetter = - mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(), - /*clearContentRequirements*/ true)) { + if (mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(), + /*clearContentRequirements*/ true)) { mDisplayModeController.updateKernelIdleTimer(displayId); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 824a55a652..a99b39a953 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -70,12 +70,12 @@ #include <common/FlagManager.h> #include "ActivePictureTracker.h" -#include "BackgroundExecutor.h" #include "Display/DisplayModeController.h" #include "Display/PhysicalDisplay.h" #include "Display/VirtualDisplaySnapshot.h" #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" +#include "DisplayHardware/HWComposer.h" #include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" #include "FrontEnd/DisplayInfo.h" @@ -102,12 +102,9 @@ #include <atomic> #include <cstdint> #include <functional> -#include <map> #include <memory> #include <mutex> #include <optional> -#include <queue> -#include <set> #include <string> #include <thread> #include <type_traits> @@ -130,7 +127,6 @@ class FlagManager; class FpsReporter; class TunnelModeEnabledReporter; class HdrLayerInfoReporter; -class HWComposer; class IGraphicBufferProducer; class Layer; class MessageBase; @@ -354,9 +350,6 @@ protected: // We're reference counted, never destroy SurfaceFlinger directly virtual ~SurfaceFlinger(); - virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&) - REQUIRES(mStateLock); - virtual std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData( BufferData& bufferData, const char* layerName, uint64_t transactionId); @@ -894,7 +887,8 @@ private: const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer = nullptr); ftl::SharedFuture<FenceResult> renderScreenImpl( - const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&, + std::unique_ptr<const RenderArea> renderArea, + const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&, const std::optional<OutputCompositionState>& displayState, const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); @@ -1045,6 +1039,8 @@ private: const sp<compositionengine::DisplaySurface>& displaySurface, const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock); void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext); + void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&) + REQUIRES(mStateLock, kMainThreadContext); void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock, kMainThreadContext); void processDisplayChanged(const wp<IBinder>& displayToken, @@ -1282,7 +1278,7 @@ private: struct HotplugEvent { hal::HWDisplayId hwcDisplayId; - hal::Connection connection = hal::Connection::INVALID; + HWComposer::HotplugEvent event; }; bool mIsHdcpViaNegVsync = false; diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index e80cd78cbd..b1552e6240 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -104,9 +104,19 @@ void FlagManager::dump(std::string& result) const { dumpFlag(result, (aconfig), #name, std::bind(&FlagManager::name, this)) #define DUMP_LEGACY_SERVER_FLAG(name) DUMP_FLAG_INTERNAL(name, false) #define DUMP_ACONFIG_FLAG(name) DUMP_FLAG_INTERNAL(name, true) +#define DUMP_SYSPROP_FLAG(name) \ + dumpFlag(result, (true), "debug.sf." #name, std::bind(&FlagManager::name, this)) base::StringAppendF(&result, "FlagManager values: \n"); + /// Sysprop flags /// + DUMP_SYSPROP_FLAG(disable_sched_fifo_sf); + DUMP_SYSPROP_FLAG(disable_sched_fifo_sf_binder); + DUMP_SYSPROP_FLAG(disable_sched_fifo_sf_sched); + DUMP_SYSPROP_FLAG(disable_sched_fifo_re); + DUMP_SYSPROP_FLAG(disable_sched_fifo_composer); + DUMP_SYSPROP_FLAG(disable_sched_fifo_composer_callback); + /// Legacy server flags /// DUMP_LEGACY_SERVER_FLAG(use_adpf_cpu_hint); DUMP_LEGACY_SERVER_FLAG(use_skia_tracing); @@ -116,6 +126,7 @@ void FlagManager::dump(std::string& result) const { DUMP_ACONFIG_FLAG(adpf_native_session_manager); DUMP_ACONFIG_FLAG(adpf_use_fmq_channel); DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout); + DUMP_ACONFIG_FLAG(increase_missed_frame_jank_threshold); DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display); /// Trunk stable readonly flags /// @@ -185,6 +196,12 @@ bool FlagManager::getServerConfigurableFlag(const char* experimentFlagName) cons const auto res = parseBool(value.c_str()); return res.has_value() && res.value(); } +#define FLAG_MANAGER_SYSPROP_FLAG(name, defaultVal) \ + bool FlagManager::name() const { \ + static const bool kFlagValue = \ + base::GetBoolProperty("debug.sf." #name, /* default value*/ defaultVal); \ + return kFlagValue; \ + } #define FLAG_MANAGER_LEGACY_SERVER_FLAG(name, syspropOverride, serverFlagName) \ bool FlagManager::name() const { \ @@ -215,6 +232,14 @@ bool FlagManager::getServerConfigurableFlag(const char* experimentFlagName) cons #define FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(name, syspropOverride, owner) \ FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, owner) +/// Debug sysprop flags - default value is always false /// +FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf, /* default */ false) +FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf_binder, /* default */ false) +FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf_sched, /* default */ false) +FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_re, /* default */ false) +FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_composer, /* default */ false) +FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_composer_callback, /* default */ false) + /// Legacy server flags /// FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "") FLAG_MANAGER_LEGACY_SERVER_FLAG(use_adpf_cpu_hint, "debug.sf.enable_adpf_cpu_hint", @@ -276,6 +301,7 @@ FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "") FLAG_MANAGER_ACONFIG_FLAG(adpf_gpu_sf, "") FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, ""); FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, ""); +FLAG_MANAGER_ACONFIG_FLAG(increase_missed_frame_jank_threshold, ""); /// Trunk stable server (R/W) flags from outside SurfaceFlinger /// FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os) diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index c7f97b4008..073302eda0 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -42,6 +42,14 @@ public: void setUnitTestMode(); + /// Debug sysprop flags /// + bool disable_sched_fifo_sf() const; + bool disable_sched_fifo_sf_binder() const; + bool disable_sched_fifo_sf_sched() const; + bool disable_sched_fifo_re() const; + bool disable_sched_fifo_composer() const; + bool disable_sched_fifo_composer_callback() const; + /// Legacy server flags /// bool test_flag() const; bool use_adpf_cpu_hint() const; @@ -53,6 +61,7 @@ public: bool adpf_use_fmq_channel() const; bool adpf_use_fmq_channel_fixed() const; bool graphite_renderengine_preview_rollout() const; + bool increase_missed_frame_jank_threshold() const; bool refresh_rate_overlay_on_external_display() const; /// Trunk stable readonly flags /// diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index 6c8972f1fb..73dfa9fa2f 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -132,7 +132,8 @@ int main(int, char**) { // Set the minimum policy of surfaceflinger node to be SCHED_FIFO. // So any thread with policy/priority lower than {SCHED_FIFO, 1}, will run // at least with SCHED_FIFO policy and priority 1. - if (errorInPriorityModification == 0) { + if (errorInPriorityModification == 0 && + !FlagManager::getInstance().disable_sched_fifo_sf_binder()) { flinger->setMinSchedulerPolicy(SCHED_FIFO, newPriority); } @@ -150,7 +151,8 @@ int main(int, char**) { // publish gui::ISurfaceComposer, the new AIDL interface sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger); - if (FlagManager::getInstance().misc1()) { + if (FlagManager::getInstance().misc1() && + !FlagManager::getInstance().disable_sched_fifo_composer()) { composerAIDL->setMinSchedulerPolicy(SCHED_FIFO, newPriority); } sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false, diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index b28d2697c5..96ab7ab614 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -190,6 +190,13 @@ flag { } # graphite_renderengine_preview_rollout flag { + name: "increase_missed_frame_jank_threshold" + namespace: "core_graphics" + description: "Increase the jank threshold to 4 milliseconds" + bug: "342265411" +} # increase_missed_frame_jank_threshold + +flag { name: "latch_unsignaled_with_auto_refresh_changed" namespace: "core_graphics" description: "Ignore eAutoRefreshChanged with latch unsignaled" diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp index c6cbe52c4a..84dc5fc4b7 100644 --- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp @@ -67,7 +67,8 @@ public: setVsyncEnabled(kHwcDisplayId, hal::IComposerClient::Vsync::DISABLE)); EXPECT_CALL(*mComposerHal, onHotplugConnect(kHwcDisplayId)); - const auto infoOpt = mComposer->onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + const auto infoOpt = + mComposer->onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected); ASSERT_TRUE(infoOpt); mDisplayId = infoOpt->id; diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 6e231aa7f4..3fead930cc 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -360,9 +360,10 @@ struct HwcDisplayVariant { // The HWC active configuration id static constexpr hal::HWConfigId HWC_ACTIVE_CONFIG_ID = 2001; - static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) { + static void injectPendingHotplugEvent(DisplayTransactionTest* test, + HWComposer::HotplugEvent event) { test->mFlinger.mutablePendingHotplugEvents().emplace_back( - TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, connection}); + TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, event}); } // Called by tests to inject a HWC display setup diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 08e426564a..54f225901e 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -202,10 +202,12 @@ public: uint32_t* maxDisplayFrames; size_t maxTokens; static constexpr pid_t kSurfaceFlingerPid = 666; - static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count(); + static constexpr nsecs_t kPresentThresholdLegacy = std::chrono::nanoseconds(2ns).count(); + static constexpr nsecs_t kPresentThresholdExtended = std::chrono::nanoseconds(4ns).count(); static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(0ns).count(); static constexpr nsecs_t kStartThreshold = std::chrono::nanoseconds(2ns).count(); - static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold, + static constexpr JankClassificationThresholds kTestThresholds{kPresentThresholdLegacy, + kPresentThresholdExtended, kDeadlineThreshold, kStartThreshold}; }; diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index ba2d3e28ad..b34de1a516 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -94,7 +94,7 @@ TEST_F(HWComposerTest, isHeadless) { constexpr hal::HWDisplayId kHwcDisplayId = 1; expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected); ASSERT_TRUE(info); ASSERT_FALSE(mHwc.isHeadless()); @@ -111,7 +111,7 @@ TEST_F(HWComposerTest, getDisplayConnectionType) { constexpr hal::HWDisplayId kHwcDisplayId = 1; expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected); ASSERT_TRUE(info); EXPECT_CALL(*mHal, getDisplayConnectionType(kHwcDisplayId, _)) @@ -133,7 +133,7 @@ TEST_F(HWComposerTest, getActiveMode) { constexpr hal::HWDisplayId kHwcDisplayId = 2; expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected); ASSERT_TRUE(info); { @@ -164,7 +164,7 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected); ASSERT_TRUE(info); ASSERT_TRUE(info->preferredDetailedTimingDescriptor.has_value()); @@ -266,7 +266,7 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) { constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected); ASSERT_TRUE(info); EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false)); @@ -364,7 +364,7 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { constexpr hal::HWConfigId kConfigId = 42; constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected); ASSERT_TRUE(info); EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(true)); @@ -452,7 +452,7 @@ TEST_F(HWComposerTest, onVsync) { constexpr hal::HWDisplayId kHwcDisplayId = 1; expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected); ASSERT_TRUE(info); const auto physicalDisplayId = info->id; diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp index 976cecbe40..35ec536d14 100644 --- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -619,14 +619,32 @@ TEST_F(LayerLifecycleManagerTest, isSimpleBufferUpdate) { } } -TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) { - // By default the layer has no buffer, so it doesn't need an input info - EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo()); +TEST_F(LayerLifecycleManagerTest, layerWithBufferNeedsInputInfo) { + // If a layer has no buffer or no color, it doesn't have an input info + LayerHierarchyTestBase::createRootLayer(3); + setColor(3, {-1._hf, -1._hf, -1._hf}); + mLifecycleManager.commitChanges(); + + EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 3)->needsInputInfo()); + + setBuffer(3); + mLifecycleManager.commitChanges(); + + EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 3)->needsInputInfo()); +} + +TEST_F(LayerLifecycleManagerTest, layerWithColorNeedsInputInfo) { + // If a layer has no buffer or no color, it doesn't have an input info + LayerHierarchyTestBase::createRootLayer(4); + setColor(4, {-1._hf, -1._hf, -1._hf}); + mLifecycleManager.commitChanges(); + + EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 4)->needsInputInfo()); - setBuffer(111); + setColor(4, {1._hf, 0._hf, 0._hf}); mLifecycleManager.commitChanges(); - EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo()); + EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 4)->needsInputInfo()); } } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index ab8c733cc8..453c053eb5 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -2045,17 +2045,17 @@ TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) { } TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) { - // By default the layer has no buffer, so we don't expect it to have an input info + // If a layer has no buffer or no color, it doesn't have an input info + setColor(111, {-1._hf, -1._hf, -1._hf}); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 12, 121, 122, 1221, 13, 2}); EXPECT_FALSE(getSnapshot(111)->hasInputInfo()); setBuffer(111); - UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); EXPECT_TRUE(getSnapshot(111)->hasInputInfo()); EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test( gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)); - EXPECT_FALSE(getSnapshot(2)->hasInputInfo()); } // content dirty test diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp index 9bf344c7af..1335640342 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp @@ -163,7 +163,7 @@ void DisplayTransactionCommitTest::processesHotplugConnectCommon() { setupCommonPreconditions<Case>(); // A hotplug connect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); // -------------------------------------------------------------------- // Call Expectations @@ -197,7 +197,7 @@ void DisplayTransactionCommitTest::ignoresHotplugConnectCommon() { setupCommonPreconditions<Case>(); // A hotplug connect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); // -------------------------------------------------------------------- // Invocation @@ -219,7 +219,7 @@ void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() { setupCommonPreconditions<Case>(); // A hotplug disconnect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected); // The display is already completely set up. Case::Display::injectHwcDisplay(this); @@ -327,9 +327,10 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimar setupCommonPreconditions<Case>(); // A hotplug connect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); // A hotplug disconnect event is also enqueued for the same display - Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + Case::Display::injectPendingHotplugEvent(this, + HWComposer::HotplugEvent::Disconnected); // -------------------------------------------------------------------- // Call Expectations @@ -378,9 +379,10 @@ TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimar existing.inject(); // A hotplug disconnect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + Case::Display::injectPendingHotplugEvent(this, + HWComposer::HotplugEvent::Disconnected); // A hotplug connect event is also enqueued for the same display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); // -------------------------------------------------------------------- // Call Expectations diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp index 4e7a1742fd..2d986c6997 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp @@ -41,9 +41,9 @@ TEST_F(HotplugTest, schedulesConfigureToProcessHotplugEvents) { const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents(); ASSERT_EQ(2u, pendingEvents.size()); EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId); - EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection); + EXPECT_EQ(HWComposer::HotplugEvent::Connected, pendingEvents[0].event); EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId); - EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection); + EXPECT_EQ(HWComposer::HotplugEvent::Disconnected, pendingEvents[1].event); } TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) { @@ -64,7 +64,7 @@ TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithIdentificationData) { using PrimaryDisplay = InnerDisplayVariant; PrimaryDisplay::setupHwcHotplugCallExpectations(this); PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this); - PrimaryDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); + PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); // TODO(b/241286146): Remove this unnecessary call. EXPECT_CALL(*mComposer, @@ -80,7 +80,7 @@ TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithIdentificationData) { using ExternalDisplay = ExternalDisplayWithIdentificationVariant; ExternalDisplay::setupHwcHotplugCallExpectations(this); ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this); - ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); + ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); // TODO(b/241286146): Remove this unnecessary call. EXPECT_CALL(*mComposer, @@ -123,7 +123,7 @@ TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithoutIdentificationData) using PrimaryDisplay = PrimaryDisplayVariant; PrimaryDisplay::setupHwcHotplugCallExpectations(this); PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this); - PrimaryDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); + PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); // TODO(b/241286146): Remove this unnecessary call. EXPECT_CALL(*mComposer, @@ -139,7 +139,7 @@ TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithoutIdentificationData) using ExternalDisplay = ExternalDisplayWithIdentificationVariant; ExternalDisplay::setupHwcHotplugCallExpectations(this); ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this); - ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); + ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); // TODO(b/241286146): Remove this unnecessary call. EXPECT_CALL(*mComposer, @@ -206,15 +206,15 @@ TEST_F(HotplugTest, ignoresDuplicateDisconnection) { // A single commit should be scheduled for both configure calls. EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); - ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); + ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); mFlinger.configure(); EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); // Disconnecting a display that was already disconnected should be a no-op. - ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED); - ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED); - ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected); + ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected); + ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected); mFlinger.configure(); // The display should be scheduled for removal during the next commit. At this point, it should @@ -249,14 +249,14 @@ TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1); - ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED); + ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected); mFlinger.configure(); // The hotplug should be rejected, so no HWComposer::DisplayData should be created. EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); // Disconnecting a display that does not exist should be a no-op. - ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected); mFlinger.configure(); EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID)); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index c2e88688e0..2353ef8db6 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -473,7 +473,7 @@ public: auto displayState = std::optional{display->getCompositionDisplay()->getState()}; auto layers = getLayerSnapshotsFn(); - return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling, + return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling, false /* grayscale */, false /* isProtected */, captureResults, displayState, layers); } diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 2bf66ac54e..7319f1ee22 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -193,6 +193,8 @@ public: MOCK_METHOD(Error, getLuts, (Display, const std::vector<sp<GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*)); + MOCK_METHOD4(getLayerPresentFences, + Error(Display, std::vector<Layer>*, std::vector<int>*, std::vector<int64_t>*)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h index 7bd85cd59f..3fa4093aa6 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h @@ -81,7 +81,7 @@ public: (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&), (override)); MOCK_METHOD(std::optional<DisplayIdentificationInfo>, onHotplug, - (hal::HWDisplayId, hal::Connection), (override)); + (hal::HWDisplayId, HWComposer::HotplugEvent), (override)); MOCK_METHOD(bool, updatesDeviceProductInfoOnHotplugReconnect, (), (const, override)); MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t)); MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, hal::Vsync), (override)); diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp index 3ddc4f2aca..536a6b352b 100644 --- a/services/vibratorservice/VibratorHalWrapper.cpp +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -131,9 +131,10 @@ HalResult<void> HalWrapper::performPwleEffect(const std::vector<PrimitivePwle>&, return HalResult<void>::unsupported(); } -HalResult<void> HalWrapper::composePwleV2(const CompositePwleV2&, const std::function<void()>&) { +HalResult<milliseconds> HalWrapper::composePwleV2(const CompositePwleV2&, + const std::function<void()>&) { ALOGV("Skipped composePwleV2 because it's not available in Vibrator HAL"); - return HalResult<void>::unsupported(); + return HalResult<milliseconds>::unsupported(); } HalResult<Capabilities> HalWrapper::getCapabilities() { @@ -359,11 +360,18 @@ HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwl return HalResultFactory::fromStatus(getHal()->composePwle(primitives, cb)); } -HalResult<void> AidlHalWrapper::composePwleV2(const CompositePwleV2& composite, - const std::function<void()>& completionCallback) { +HalResult<milliseconds> AidlHalWrapper::composePwleV2( + const CompositePwleV2& composite, const std::function<void()>& completionCallback) { // This method should always support callbacks, so no need to double check. auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback); - return HalResultFactory::fromStatus(getHal()->composePwleV2(composite, cb)); + + milliseconds totalDuration(0); + for (const auto& primitive : composite.pwlePrimitives) { + totalDuration += milliseconds(primitive.timeMillis); + } + + return HalResultFactory::fromStatus<milliseconds>(getHal()->composePwleV2(composite, cb), + totalDuration); } HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() { diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h index 339a6e1d0d..9a39ad4f7b 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -423,8 +423,8 @@ public: virtual HalResult<void> performPwleEffect(const std::vector<PrimitivePwle>& primitives, const std::function<void()>& completionCallback); - virtual HalResult<void> composePwleV2(const CompositePwleV2& composite, - const std::function<void()>& completionCallback); + virtual HalResult<std::chrono::milliseconds> composePwleV2( + const CompositePwleV2& composite, const std::function<void()>& completionCallback); protected: // Shared pointer to allow CallbackScheduler to outlive this wrapper. @@ -511,8 +511,9 @@ public: const std::vector<PrimitivePwle>& primitives, const std::function<void()>& completionCallback) override final; - HalResult<void> composePwleV2(const CompositePwleV2& composite, - const std::function<void()>& completionCallback) override final; + HalResult<std::chrono::milliseconds> composePwleV2( + const CompositePwleV2& composite, + const std::function<void()>& completionCallback) override final; protected: HalResult<Capabilities> getCapabilitiesInternal() override final; diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp index c58e05cedb..7545148ee1 100644 --- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp @@ -787,5 +787,6 @@ TEST_F(VibratorHalWrapperAidlTest, TestComposePwleV2) { result = mWrapper->composePwleV2(composite, callback); ASSERT_TRUE(result.isOk()); + ASSERT_EQ(300ms, result.value()); ASSERT_EQ(1, *callbackCounter.get()); } diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 3cb94050af..8c0cce26ba 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -38,6 +38,12 @@ namespace { +/* + * Annotation to tell clang that we intend to fall through from one case to + * another in a switch. Sourced from android-base/macros.h. + */ +#define FALLTHROUGH_INTENDED [[clang::fallthrough]] + inline bool IsIntegral(double value) { #if defined(ANDROID) // Android NDK doesn't provide std::trunc yet diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h index 28de680a99..5818c73b16 100644 --- a/vulkan/vkjson/vkjson.h +++ b/vulkan/vkjson/vkjson.h @@ -33,12 +33,6 @@ #undef max #endif -/* - * Annotation to tell clang that we intend to fall through from one case to - * another in a switch. Sourced from android-base/macros.h. - */ -#define FALLTHROUGH_INTENDED [[clang::fallthrough]] - struct VkJsonLayer { VkLayerProperties properties; std::vector<VkExtensionProperties> extensions; |