Merge "Tracks whether an injected MotionEvent came from an accessibility tool." into main
diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl
index 248e973..9b11109 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 feca18d..888fb67 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -194,7 +194,6 @@
#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 @@
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 4486bd6..db56551 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -119,7 +119,6 @@
*/
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 @@
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 c86adef..c818e0d 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 2bd527c..58257ba 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 90427bd..f3f5148 100644
--- a/include/input/DisplayTopologyGraph.h
+++ b/include/input/DisplayTopologyGraph.h
@@ -43,7 +43,7 @@
struct DisplayTopologyAdjacentDisplay {
ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
DisplayTopologyPosition position;
- float offsetPx;
+ float offsetDp;
};
/**
@@ -52,6 +52,7 @@
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 aac369d..fb00d4f 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -269,6 +269,7 @@
"-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 0a22588..bc7ae37 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 = 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 @@
// 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 444f061..c13e0f9 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 @@
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 = 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 0000000..b75493c
--- /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 623e7b9..1c1b6f3 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 @@
{
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 @@
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 @@
void IPCThreadState::stopProcess(bool /*immediate*/)
{
//ALOGI("**** STOPPING PROCESS");
- flushCommands();
+ (void)flushCommands();
int fd = mProcess->mDriverFD;
mProcess->mDriverFD = -1;
close(fd);
@@ -1215,7 +1233,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 719e445..c9ca646 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 @@
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 c0ebee0..777c22a 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -299,8 +299,13 @@
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 @@
}
#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 @@
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 @@
}
#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 @@
}
#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 @@
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 @@
#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 @@
#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 0e1e9b4..0bec379 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 @@
}
}
-#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 fe6e1a3..03d974d 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 @@
}
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 @@
// 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 99fa6b8..51b9716b 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 8404a48..adef9ea 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -16,6 +16,7 @@
"libdowncast_rs",
"liblibc",
"liblog_rust",
+ "libzerocopy",
],
host_supported: true,
vendor_available: true,
@@ -205,6 +206,7 @@
"libdowncast_rs",
"liblibc",
"liblog_rust",
+ "libzerocopy",
],
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index e08a763..0026f21 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -99,6 +99,8 @@
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 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 485b0bd..2d40ced 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -184,7 +184,7 @@
/// 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 @@
}
// 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 @@
/// 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 @@
}
// 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 @@
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 @@
}
// 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<'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 0000000..8639c0d
--- /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 557f0e8..c19e375 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 @@
#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 1164767..dcd6461 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -34,6 +34,8 @@
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 9f656ec..e88e3f3 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -711,6 +711,35 @@
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 dc22647..6e00246 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -348,6 +348,10 @@
*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 c6fd487..d227e6e 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -209,6 +209,18 @@
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 @@
// 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 c74ba0a..65ad896 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -47,6 +47,71 @@
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 @@
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 6e20b8a..da0f2ed 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 d2a1fc0..55ff49c 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 22cba44..caf3117 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 @@
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 c4a758a..6f454be 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 @@
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 6bf79d4..a8acded 100644
--- a/libs/debugstore/rust/src/core.rs
+++ b/libs/debugstore/rust/src/core.rs
@@ -48,7 +48,7 @@
///
/// 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 @@
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 2ad7f4e..47760f3 100644
--- a/libs/debugstore/rust/src/storage.rs
+++ b/libs/debugstore/rust/src/storage.rs
@@ -32,14 +32,18 @@
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 @@
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 @@
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 @@
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 @@
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 af50a29..dce7778 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -21,10 +21,27 @@
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 @@
"-Werror",
],
+ static_libs: [
+ "graphicsenv_flags_c_lib",
+ ],
+
shared_libs: [
"libbase",
"libbinder",
@@ -42,6 +63,7 @@
"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 0000000..6974da9
--- /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 4874dbd..4bc2611 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 @@
}
} // namespace
+namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags;
+
namespace android {
enum NativeLibrary {
@@ -624,10 +627,36 @@
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 0000000..ac66362
--- /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 0000000..2b94187
--- /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 452e48b..55fa13a 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 @@
// 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 @@
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 38465b0..0848fac 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -244,12 +244,6 @@
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 @@
#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 @@
}
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 @@
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 f012586..270bfbd 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -477,9 +477,14 @@
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 @@
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 504509d..3ad0e52 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -656,9 +656,13 @@
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 @@
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 168129b..052b8ed 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -417,18 +417,18 @@
}
#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 @@
// 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 @@
}
} 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 @@
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 @@
}
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 c1b6568..e133532 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -85,6 +85,12 @@
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 @@
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 2beeae0..852885b 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(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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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;
+ uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd;
+ if ((mFlags & wakeupFlags) == wakeupFlags) {
+ mFlags &= ~(wakeupFlags);
}
- if (mEarlyWakeupEnd && !mEarlyWakeupStart) {
- flags |= ISurfaceComposer::eEarlyWakeupEnd;
- }
-
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 @@
}
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 @@
// ---------------------------------------------------------------------------
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 f126c0b..b735418 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -141,7 +141,8 @@
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 1bc1dd0..b97a496 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -90,8 +90,6 @@
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 @@
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 @@
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 e00c44e..f99b54b 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -65,13 +65,14 @@
// 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 @@
// 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 @@
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 5cd19c1..acb0006 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -245,10 +245,13 @@
// 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 30cbfa2..dbf707f 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -271,6 +271,7 @@
#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 @@
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 @@
// 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 56eb291..f6b3e89 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -139,12 +139,17 @@
// * 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 d20b346..d30a830 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -455,8 +455,8 @@
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 @@
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 98f24c2..8dfd3cb 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 @@
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 394a5cf..90d91ac 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -142,3 +142,11 @@
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 53f4a36..e6ee89f 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -81,7 +81,9 @@
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 06f00a4..8062a2e 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -85,6 +85,7 @@
// 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 @@
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 0b47f3e..1ed9794 100644
--- a/libs/input/AccelerationCurve.cpp
+++ b/libs/input/AccelerationCurve.cpp
@@ -47,9 +47,9 @@
// 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 bf928f4..72a6fdf 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -79,13 +79,6 @@
}
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 @@
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 ab74efb..26a8d8f 100644
--- a/libs/input/rust/keyboard_classification_config.rs
+++ b/libs/input/rust/keyboard_classification_config.rs
@@ -20,6 +20,15 @@
// 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 @@
(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 @@
(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 3c789b4..1b89a5c 100644
--- a/libs/input/rust/keyboard_classifier.rs
+++ b/libs/input/rust/keyboard_classifier.rs
@@ -66,11 +66,11 @@
/// 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 @@
/// 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 e2eb080..5bb1d56 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 @@
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 444722b..226a8a6 100644
--- a/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
+++ b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
@@ -113,18 +113,11 @@
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 @@
* 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 @@
* 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 006a785..253aa18 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -343,9 +343,13 @@
* 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 3959fce..fad0f6c 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -221,7 +221,11 @@
}
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 @@
// 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 @@
} 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 @@
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 @@
}
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 60e87b5..1ffd382 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 @@
}
// 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 ce232cc..c0a1cc5 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -178,13 +178,21 @@
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 d760285..aeb2603 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -541,7 +541,7 @@
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 14d08ee..5f2d1b1 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -838,8 +838,7 @@
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 @@
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 @@
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 @@
// 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 cd1bd71..8edf98e 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -90,6 +90,8 @@
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 8c52c57..b03ebe3 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -64,7 +64,7 @@
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 ef57c30..897f5cf 100644
--- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
@@ -167,14 +167,14 @@
}
// 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 defaf6e..f71a63d 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -70,7 +70,7 @@
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 aa12cef..5dc36e6 100644
--- a/libs/renderengine/skia/filters/MouriMap.cpp
+++ b/libs/renderengine/skia/filters/MouriMap.cpp
@@ -104,7 +104,7 @@
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 ea5605d..67f4aa1 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 @@
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 7347ac7..4929b3f 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 4a6e849..800bf3c 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 @@
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 0dab517..79fb704 100644
--- a/libs/tracing_perfetto/tests/Android.bp
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -21,44 +21,12 @@
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 @@
"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 02e8d10..c97e900 100644
--- a/libs/tracing_perfetto/tracing_sdk.cpp
+++ b/libs/tracing_perfetto/tracing_sdk.cpp
@@ -254,6 +254,47 @@
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 c9f0761..b7ef9b3 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 @@
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 @@
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 8a14db8..13ed754 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 @@
// 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 @@
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 @@
// 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 @@
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 @@
}
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 cdac269..1e3449c 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -74,6 +74,7 @@
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 @@
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 ef686df..090d2ee 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -26,7 +26,6 @@
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 @@
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 d1699e7..75c71a5 100644
--- a/libs/ui/tests/DisplayIdentification_test.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -376,6 +376,22 @@
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, 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 91250b9..eb747c7 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -8,6 +8,11 @@
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_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 @@
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 b1a287f..5fe9484 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 @@
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());
}
- features.push_back(0);
- attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
- attrs.push_back(reinterpret_cast<EGLAttrib>(features.data()));
+
+ // 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()));
+ }
}
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 99360b9..8db9a78 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -35,11 +35,11 @@
{
}
- // 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 @@
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 ca9fe5e..689221f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -19,10 +19,16 @@
],
}
+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 @@
"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 07c681f..a3afca5 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 0000000..cda67f0
--- /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 0000000..1ad637c
--- /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 0000000..b1f1867
--- /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 0000000..4d4bf28
--- /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 3072885..057d127 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 @@
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 8056a2c..d2184d8 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -21,17 +21,71 @@
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 @@
"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 0000000..65a1b58
--- /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 0000000..726779e
--- /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 0000000..cf6a67e
--- /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 b155122..7d62ed9 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -41,8 +41,6 @@
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 @@
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 @@
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 5bf6ebb..9f91285 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -1017,9 +1017,9 @@
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 717d26c..493e480 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -918,6 +918,21 @@
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,
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 @@
: 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 @@
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 @@
}
// 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 @@
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::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 @@
}
}
- 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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
targets);
// Make a slippery entrance into the new window.
- isSplit = !isFromMouse;
ftl::Flags<InputTarget::Flags> targetFlags;
if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) {
@@ -2656,9 +2645,9 @@
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 @@
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 @@
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::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
ui::LogicalDisplayId displayId) {
- auto monitorsIt = mGlobalMonitorsByDisplay.find(displayId);
- if (monitorsIt == mGlobalMonitorsByDisplay.end()) return;
+ mConnectionManager
+ .forEachGlobalMonitorConnection(displayId,
+ [&](const std::shared_ptr<Connection>& connection) {
+ if (!connection->responsive) {
+ ALOGW("Ignoring unrsponsive monitor: %s",
+ connection->getInputChannelName()
+ .c_str());
+ 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);
- }
+ 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 @@
*
* 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 @@
}
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 @@
}
}
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 @@
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 @@
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::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 @@
}
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
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 @@
// 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 @@
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 @@
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 @@
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 @@
{ // 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 @@
}
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 @@
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 @@
bool notify) {
LOG_ALWAYS_FATAL_IF(connection == nullptr);
abortBrokenDispatchCycleLocked(connection, notify);
- removeConnectionLocked(connection);
- if (connection->monitor) {
- removeMonitorChannelLocked(connection->getToken());
- }
+ mAnrTracker.eraseToken(connection->getToken());
+ mConnectionManager.removeConnection(connection);
- mLooper->removeFd(connection->inputPublisher.getChannel().getFd());
-
- 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::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 @@
} // 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
-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 @@
}
}
+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 bca1c67..415f4c8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -224,6 +224,132 @@
/** 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 @@
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 @@
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 @@
};
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 @@
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 @@
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 @@
// 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 @@
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 881a96b..4da05c1 100644
--- a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp
+++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp
@@ -133,10 +133,11 @@
}
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 cc04684..0000000
--- 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 887913f..c19d278 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
@@ -26,20 +26,214 @@
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 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);
- static impl::TraceConfig parseConfig(proto::AndroidInputEventConfig::Decoder& protoConfig);
+ 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 761d619..2ff6e1c 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -50,7 +50,7 @@
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 @@
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 @@
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 77b5c2e..ebcd9c9 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -34,6 +34,11 @@
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 @@
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 @@
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 @@
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 @@
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 f54b76b..608bec4 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -150,6 +150,11 @@
// 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 @@
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 24919b6..207806d 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -1085,7 +1085,7 @@
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 1403ca2..931766b 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -154,7 +154,7 @@
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 e0e0ac2..20ed74f 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -55,7 +55,7 @@
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 9f584a0..e21c2f9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -419,7 +419,7 @@
}
}
-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 8319922..301632f 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -63,7 +63,7 @@
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 d4a86ac..630c3d9 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -66,11 +66,12 @@
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 @@
[[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 fe3e4c2..400792b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -104,14 +104,14 @@
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 @@
}
}
-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 7d9b3e4..9e2a81b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -47,7 +47,7 @@
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 @@
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 6efaeca..5cfda03 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -3666,7 +3666,7 @@
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 @@
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 eb4326f..96fc61b 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -185,7 +185,7 @@
[[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 @@
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 0c094e6..18a7102 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -59,9 +59,11 @@
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 @@
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 @@
return out;
}
-std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() const {
return mDisplayId;
}
@@ -510,4 +514,12 @@
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 a2c4be9..9f53a7b 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -66,10 +66,12 @@
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 827076a..480e276 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 @@
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 @@
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> 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> 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 @@
// 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 @@
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::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 be76b61..ae85e3a 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -92,6 +92,8 @@
[[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 @@
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 600ae52..18d47f6 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -40,14 +40,15 @@
// 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 @@
"HardwareStateConverter_test.cpp",
"InputDeviceMetricsCollector_test.cpp",
"InputDeviceMetricsSource_test.cpp",
- "InputMapperTest.cpp",
- "InputProcessor_test.cpp",
- "InputProcessorConverter_test.cpp",
"InputDispatcher_test.cpp",
+ "InputMapperTest.cpp",
+ "InputProcessorConverter_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 @@
"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 @@
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 @@
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 0000000..1fd6cee
--- /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 18e0b30..594ee3b 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -141,9 +141,9 @@
*/
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 @@
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 67b1e8c..5a14f4b 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -31,6 +31,30 @@
} // 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 @@
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 42c9567..9dce31a 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 @@
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 8fa439d..fd9884b 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_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 @@
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 @@
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 5c82319..368db1b 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -8101,6 +8101,17 @@
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 @@
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 @@
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 8235c90..652a658 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -30,7 +30,7 @@
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 @@
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 @@
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 10ef6f1..edc87a3 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -40,8 +40,8 @@
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 470e65b..a8e4736 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -385,30 +385,29 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
/*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 @@
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 @@
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 @@
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 @@
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, 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_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, 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_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 ac616d0..dce5472 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -68,7 +68,7 @@
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 @@
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 @@
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 1dd32c4..17acdd4 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -1085,10 +1085,9 @@
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 @@
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 ca0f1e8..1cfaaa8 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -57,6 +57,7 @@
}
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 @@
/*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 @@
.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 @@
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 d15048d..b7cb348 100644
--- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -53,8 +53,9 @@
*/
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 @@
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 157bee3..548df22 100644
--- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
+++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
@@ -89,9 +89,9 @@
*/
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 ea69fff..5f5aa63 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 @@
*/
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 @@
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 a1da39a..846260a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -346,7 +346,7 @@
}
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 7347ac7..3ccdc17 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 05f7d1b..ea7d6d7 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,15 +26,15 @@
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 @@
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 @@
"libsync",
"libui",
"libutils",
- "libSurfaceFlingerProp",
- "libaconfig_storage_read_api_cc",
],
static_libs: [
"iinputflinger_aidl_lib_static",
@@ -105,11 +105,11 @@
"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 @@
"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 @@
filegroup {
name: "libsurfaceflinger_backend_sources",
srcs: [
- "PowerAdvisor/*.cpp",
"DisplayHardware/AidlComposerHal.cpp",
"DisplayHardware/ComposerHal.cpp",
"DisplayHardware/FramebufferSurface.cpp",
@@ -185,6 +184,7 @@
"DisplayHardware/HWComposer.cpp",
"DisplayHardware/HidlComposerHal.cpp",
"DisplayHardware/VirtualDisplaySurface.cpp",
+ "PowerAdvisor/*.cpp",
],
}
@@ -208,21 +208,20 @@
"DisplayDevice.cpp",
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
- "FrontEnd/LayerCreationArgs.cpp",
- "FrontEnd/LayerHandle.cpp",
- "FrontEnd/LayerSnapshot.cpp",
- "FrontEnd/LayerSnapshotBuilder.cpp",
- "FrontEnd/LayerHierarchy.cpp",
- "FrontEnd/LayerLifecycleManager.cpp",
- "FrontEnd/RequestedLayerState.cpp",
- "FrontEnd/TransactionHandler.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
+ "FrontEnd/LayerCreationArgs.cpp",
+ "FrontEnd/LayerHandle.cpp",
+ "FrontEnd/LayerHierarchy.cpp",
+ "FrontEnd/LayerLifecycleManager.cpp",
+ "FrontEnd/LayerSnapshot.cpp",
+ "FrontEnd/LayerSnapshotBuilder.cpp",
+ "FrontEnd/RequestedLayerState.cpp",
+ "FrontEnd/TransactionHandler.cpp",
"HdrLayerInfoReporter.cpp",
"HdrSdrRatioOverlay.cpp",
"Jank/JankTracker.cpp",
- "WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerFE.cpp",
"LayerProtoHelper.cpp",
@@ -234,10 +233,10 @@
"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 @@
"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 @@
"android.hardware.configstore@1.1",
"android.hardware.graphics.common@1.2",
"libhidlbase",
+ "liblog",
"libui",
"libutils",
- "liblog",
],
static_libs: [
"libSurfaceFlingerProperties",
@@ -354,10 +354,10 @@
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 8529c72..bb6bebe 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 @@
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 @@
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 @@
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 82006f4..5fcc8b0 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 @@
// 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 @@
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 @@
// 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 d69a923..1e4132c 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -26,7 +26,7 @@
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 6e431bb..018ee6e 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -157,6 +157,10 @@
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 e63a14b..252c6b6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -640,7 +640,15 @@
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 7f94428..c47943f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -138,13 +138,13 @@
}
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 b1b997a..d60f6ff 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -231,11 +231,12 @@
// 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 @@
// 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 ec15539..a010353 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 @@
}
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 @@
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 cacdb8c..86ca4b1 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -214,6 +214,10 @@
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 86d7388..fece312 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -611,7 +611,11 @@
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 @@
} 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 @@
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 @@
? 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 @@
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 @@
}
} 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 @@
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 @@
} 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 a47bd57..9fedb57 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -107,7 +107,10 @@
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 7289e2f..86ef6ca 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -264,28 +264,21 @@
}
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.
+ // 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);
- }
+ // 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 @@
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 591ebb2..1d53e71 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -573,7 +573,7 @@
return false;
}
- if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+ if (hasBufferOrSidebandStream() || fillsColor()) {
return true;
}
@@ -586,6 +586,15 @@
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 dd861a7..7232379 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -88,6 +88,8 @@
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 c6d7160..0efc396 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -344,7 +344,8 @@
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 20c58eb..6a5eeba 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 @@
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 1f8557c..d117753 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::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::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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
})
.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::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 @@
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 824a55a6..a99b39a 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 FpsReporter;
class TunnelModeEnabledReporter;
class HdrLayerInfoReporter;
-class HWComposer;
class IGraphicBufferProducer;
class Layer;
class MessageBase;
@@ -354,9 +350,6 @@
// 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 @@
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 @@
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 @@
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 e80cd78..b1552e6 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -104,9 +104,19 @@
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 @@
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 @@
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 @@
#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(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 c7f97b4..073302e 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -42,6 +42,14 @@
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 @@
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 6c8972f..73dfa9f 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -132,7 +132,8 @@
// 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 @@
// 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 b28d269..96ab7ab 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -190,6 +190,13 @@
} # 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 c6cbe52..84dc5fc 100644
--- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -67,7 +67,8 @@
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 6e231aa..3fead93 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -360,9 +360,10 @@
// 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 08e4265..54f2259 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -202,10 +202,12 @@
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 ba2d3e2..b34de1a 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -94,7 +94,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 976cecb..35ec536 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -619,14 +619,32 @@
}
}
-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());
-
- setBuffer(111);
+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_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+ 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());
+
+ setColor(4, {1._hf, 0._hf, 0._hf});
+ mLifecycleManager.commitChanges();
+
+ 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 ab8c733..453c053 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -2045,17 +2045,17 @@
}
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 9bf344c..1335640 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -163,7 +163,7 @@
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 @@
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 @@
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 @@
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 @@
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 4e7a174..2d986c6 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -41,9 +41,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 c2e8868..2353ef8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -473,7 +473,7 @@
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 2bf66ac..7319f1e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -193,6 +193,8 @@
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 7bd85cd..3fa4093 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -81,7 +81,7 @@
(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 3ddc4f2..536a6b3 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -131,9 +131,10 @@
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 @@
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 339a6e1..9a39ad4 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -423,8 +423,8 @@
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 @@
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 c58e05c..7545148 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -787,5 +787,6 @@
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 3cb9405..8c0cce2 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 28de680..5818c73 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;