Merge "Get service PID from dumpsys in bugreports"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 54b138f..8c77255 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1896,6 +1896,9 @@
}
ds.AddDir(PREREBOOT_DATA_DIR, false);
add_mountinfo();
+ for (const char* path : {"/proc/cpuinfo", "/proc/meminfo"}) {
+ ds.AddZipEntry(ZIP_ROOT_DIR + path, path);
+ }
DumpIpTablesAsRoot();
DumpDynamicPartitionInfo();
ds.AddDir(OTA_METADATA_DIR, true);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index ce3d669..794750f 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1917,10 +1917,11 @@
// Open the reference profile if needed.
UniqueFile reference_profile = maybe_open_reference_profile(
pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
-
- if (reference_profile.fd() == -1) {
- // We don't create an app image without reference profile since there is no speedup from
- // loading it in that case and instead will be a small overhead.
+ struct stat sbuf;
+ if (reference_profile.fd() == -1 ||
+ (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) {
+ // We don't create an app image with empty or non existing reference profile since there
+ // is no speedup from loading it in that case and instead will be a small overhead.
generate_app_image = false;
}
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 6a3120c..bf2c0d1 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -308,7 +308,7 @@
// This is different from the normal installd. We only do the base
// directory, the rest will be created on demand when each app is compiled.
if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) {
- LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
+ PLOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
return false;
}
@@ -460,7 +460,7 @@
// this tool will wipe the OTA artifact cache and try again (for robustness after
// a failed OTA with remaining cache artifacts).
if (access(apk_path, F_OK) != 0) {
- LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
+ PLOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
return true;
}
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c62734a..1b7acab 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -45,6 +45,10 @@
namespace android {
namespace installd {
+// We don't know the filesystem types of the partitions in the update package,
+// so just try the possibilities one by one.
+static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
+
static void CloseDescriptor(int fd) {
if (fd >= 0) {
int result = close(fd);
@@ -82,6 +86,27 @@
}
}
+static bool TryMountWithFstypes(const char* block_device, const char* target) {
+ for (int i = 0; i < kTryMountFsTypes.size(); ++i) {
+ const char* fstype = kTryMountFsTypes[i];
+ int mount_result = mount(block_device, target, fstype, MS_RDONLY, /* data */ nullptr);
+ if (mount_result == 0) {
+ return true;
+ }
+ if (errno == EINVAL && i < kTryMountFsTypes.size() - 1) {
+ // Only try the next fstype if mounting failed due to the current one
+ // being invalid.
+ LOG(WARNING) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype << " - trying " << kTryMountFsTypes[i + 1];
+ } else {
+ PLOG(ERROR) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype;
+ return false;
+ }
+ }
+ __builtin_unreachable();
+}
+
static void TryExtraMount(const char* name, const char* slot, const char* target) {
std::string partition_name = StringPrintf("%s%s", name, slot);
@@ -91,12 +116,7 @@
if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
std::string path;
if (dm.GetDmDevicePathByName(partition_name, &path)) {
- int mount_result = mount(path.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- if (mount_result == 0) {
+ if (TryMountWithFstypes(path.c_str(), target)) {
return;
}
}
@@ -105,12 +125,7 @@
// Fall back and attempt a direct mount.
std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
- int mount_result = mount(block_device.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- UNUSED(mount_result);
+ (void)TryMountWithFstypes(block_device.c_str(), target);
}
// Entry for otapreopt_chroot. Expected parameters are:
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index f950276..db5c34e 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -60,6 +60,11 @@
i=0
while ((i<MAXIMUM_PACKAGES)) ; do
+ DONE=$(cmd otadexopt done)
+ if [ "$DONE" = "OTA complete." ] ; then
+ break
+ fi
+
DEXOPT_PARAMS=$(cmd otadexopt next)
/system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
@@ -67,13 +72,8 @@
PROGRESS=$(cmd otadexopt progress)
print -u${STATUS_FD} "global_progress $PROGRESS"
- DONE=$(cmd otadexopt done)
- if [ "$DONE" = "OTA incomplete." ] ; then
- sleep 1
- i=$((i+1))
- continue
- fi
- break
+ sleep 1
+ i=$((i+1))
done
DONE=$(cmd otadexopt done)
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 3b589dc..5c4e1a4 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -185,7 +185,7 @@
std::optional<std::string> volume_uuid_;
std::string package_name_;
std::string apk_path_;
- std::string empty_dm_file_;
+ std::string dm_file_;
std::string app_apk_dir_;
std::string app_private_dir_ce_;
std::string app_private_dir_de_;
@@ -248,26 +248,6 @@
<< " : " << error_msg;
}
- // Create an empty dm file.
- empty_dm_file_ = apk_path_ + ".dm";
- {
- int fd = open(empty_dm_file_.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- return ::testing::AssertionFailure() << "Could not open " << empty_dm_file_;
- }
- FILE* file = fdopen(fd, "wb");
- if (file == nullptr) {
- return ::testing::AssertionFailure() << "Null file for " << empty_dm_file_
- << " fd=" << fd;
- }
- ZipWriter writer(file);
- // Add vdex to zip.
- writer.StartEntry("primary.prof", ZipWriter::kCompress);
- writer.FinishEntry();
- writer.Finish();
- fclose(file);
- }
-
// Create the app user data.
binder::Status status = service_->createAppData(
volume_uuid_,
@@ -316,6 +296,46 @@
<< secondary_dex_de_ << " : " << error_msg;
}
+ // Create a non-empty dm file.
+ dm_file_ = apk_path_ + ".dm";
+ {
+ android::base::unique_fd fd(open(dm_file_.c_str(),
+ O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR));
+ if (fd.get() < 0) {
+ return ::testing::AssertionFailure() << "Could not open " << dm_file_;
+ }
+ FILE* file = fdopen(fd.release(), "wb");
+ if (file == nullptr) {
+ return ::testing::AssertionFailure() << "Null file for " << dm_file_
+ << " fd=" << fd.get();
+ }
+
+ // Create a profile file.
+ std::string profile_file = app_private_dir_ce_ + "/primary.prof";
+ run_cmd("profman --generate-test-profile=" + profile_file);
+
+ // Add profile to zip.
+ ZipWriter writer(file);
+ writer.StartEntry("primary.prof", ZipWriter::kCompress);
+ android::base::unique_fd profile_fd(open(profile_file.c_str(), O_RDONLY));
+ if (profile_fd.get() < 0) {
+ return ::testing::AssertionFailure() << "Failed to open profile '"
+ << profile_file << "'";
+ }
+ std::string profile_content;
+ if (!android::base::ReadFdToString(profile_fd, &profile_content)) {
+ return ::testing::AssertionFailure() << "Failed to read profile "
+ << profile_file << "'";
+ }
+ writer.WriteBytes(profile_content.c_str(), profile_content.length());
+ writer.FinishEntry();
+ writer.Finish();
+ fclose(file);
+
+ // Delete the temp file.
+ unlink(profile_file.c_str());
+ }
+
// Fix app data uid.
status = service_->fixupAppData(volume_uuid_, kTestUserId);
if (!status.isOk()) {
@@ -608,7 +628,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
int64_t odex_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_,
@@ -657,13 +677,13 @@
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC |
DEXOPT_GENERATE_APP_IMAGE,
oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
- /*binder_result=*/nullptr, empty_dm_file_.c_str());
+ /*binder_result=*/nullptr, dm_file_.c_str());
checkVisibility(in_dalvik_cache, ODEX_IS_PUBLIC);
CompilePrimaryDexOk("speed-profile",
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE,
oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH,
- /*binder_result=*/nullptr, empty_dm_file_.c_str());
+ /*binder_result=*/nullptr, dm_file_.c_str());
checkVisibility(in_dalvik_cache, ODEX_IS_PRIVATE);
}
};
@@ -787,7 +807,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryProfilePublic) {
@@ -799,7 +819,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) {
@@ -811,7 +831,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptBlockPrimary) {
@@ -874,7 +894,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -893,7 +913,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -926,7 +946,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
// Enable the property and use dex2oat64.
ASSERT_TRUE(android::base::SetProperty(property, "true")) << property;
CompilePrimaryDexOk("speed-profile",
@@ -936,7 +956,7 @@
kTestAppGid,
DEX2OAT_FROM_SCRATCH,
/*binder_result=*/nullptr,
- empty_dm_file_.c_str());
+ dm_file_.c_str());
}
class PrimaryDexReCompilationTest : public DexoptTest {
@@ -1143,7 +1163,7 @@
service_->prepareAppProfile(package_name, has_user_id ? kTestUserId : USER_NULL,
kTestAppId, profile_name, apk_path_,
has_dex_metadata ? std::make_optional<std::string>(
- empty_dm_file_)
+ dm_file_)
: std::nullopt,
&result));
ASSERT_EQ(expected_result, result);
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index eadf678..bec262e 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -223,8 +223,13 @@
#endif // !VENDORSERVICEMANAGER
ServiceManager::Service::~Service() {
- if (!hasClients) {
- // only expected to happen on process death
+ if (hasClients) {
+ // only expected to happen on process death, we don't store the service
+ // name this late (it's in the map that holds this service), but if it
+ // is happening, we might want to change 'unlinkToDeath' to explicitly
+ // clear this bit so that we can abort in other cases, where it would
+ // mean inconsistent logic in servicemanager (unexpected and tested, but
+ // the original lazy service impl here had that bug).
LOG(WARNING) << "a service was removed when there are clients";
}
}
diff --git a/include/input/Input.h b/include/input/Input.h
index 7ea2970..1eda981 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -818,6 +818,8 @@
const PointerCoords&);
static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
const PointerCoords&);
+ // The rounding precision for transformed motion events.
+ static constexpr float ROUNDING_PRECISION = 0.001f;
protected:
int32_t mAction;
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 51b97165..ef58ed3 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -127,8 +127,7 @@
t.mData.mInterfaceName = std::string(String8(interfaceName).string());
if (interfaceName.size() != t.mData.mInterfaceName.size()) {
LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
- "utf-8: "
- << interfaceName;
+ "utf-8.";
return std::nullopt;
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 233a8e4..fbad0f7 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -20,6 +20,7 @@
#include <dlfcn.h>
#include <inttypes.h>
+#include <netinet/tcp.h>
#include <poll.h>
#include <unistd.h>
@@ -608,6 +609,18 @@
return -savedErrno;
}
+ if (addr.addr()->sa_family == AF_INET || addr.addr()->sa_family == AF_INET6) {
+ int noDelay = 1;
+ int result =
+ setsockopt(serverFd.get(), IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay));
+ if (result < 0) {
+ int savedErrno = errno;
+ ALOGE("Could not set TCP_NODELAY on %s: %s", addr.toString().c_str(),
+ strerror(savedErrno));
+ return -savedErrno;
+ }
+ }
+
RpcTransportFd transportFd(std::move(serverFd));
if (0 != TEMP_FAILURE_RETRY(connect(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 2b0e5ba..38bd081 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -1036,8 +1036,8 @@
return DEAD_OBJECT;
}
- if (it->second.asyncTodo.size() == 0) return OK;
- if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+ if (it->second.asyncTodo.size() != 0 &&
+ it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64,
it->second.asyncNumber, addr);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 1488400..07b38d7 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -119,5 +119,10 @@
{
"name": "memunreachable_binder_test"
}
+ ],
+ "imports": [
+ {
+ "path": "packages/modules/Virtualization"
+ }
]
}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index a3ed571..1164767 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -80,4 +80,8 @@
// get queued.
oneway void blockingSendFdOneway(in ParcelFileDescriptor fd);
ParcelFileDescriptor blockingRecvFd();
+
+ // Same as blockingSendFdOneway, but with integers.
+ oneway void blockingSendIntOneway(int n);
+ int blockingRecvInt();
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 6e34d25..9f54087 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -129,7 +129,7 @@
static std::string allocateSocketAddress() {
static size_t id = 0;
std::string temp = getenv("TMPDIR") ?: "/tmp";
- auto ret = temp + "/binderRpcTest_" + std::to_string(id++);
+ auto ret = temp + "/binderRpcTest_" + std::to_string(getpid()) + "_" + std::to_string(id++);
unlink(ret.c_str());
return ret;
};
@@ -237,9 +237,13 @@
std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
if (singleThreaded) {
ret += "_single_threaded";
+ } else {
+ ret += "_multi_threaded";
}
if (noKernel) {
ret += "_no_kernel";
+ } else {
+ ret += "_with_kernel";
}
return ret;
}
@@ -435,8 +439,7 @@
for (auto& t : ts) t.join();
}
-static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
- size_t sleepMs = 500) {
+static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, size_t sleepMs) {
size_t epochMsBefore = epochMillis();
std::vector<std::thread> ts;
@@ -462,7 +465,7 @@
constexpr size_t kNumThreads = 10;
constexpr size_t kNumCalls = kNumThreads + 3;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
}
TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
@@ -475,7 +478,7 @@
constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
auto proc = createRpcTestSocketServerProcess(
{.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
}
TEST_P(BinderRpc, ThreadingStressTest) {
@@ -483,9 +486,9 @@
GTEST_SKIP() << "This test requires multiple threads";
}
- constexpr size_t kNumClientThreads = 10;
- constexpr size_t kNumServerThreads = 10;
- constexpr size_t kNumCalls = 100;
+ constexpr size_t kNumClientThreads = 5;
+ constexpr size_t kNumServerThreads = 5;
+ constexpr size_t kNumCalls = 50;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
@@ -584,30 +587,22 @@
GTEST_SKIP() << "This test requires multiple threads";
}
- constexpr size_t kNumSleeps = 10;
+ constexpr size_t kNumQueued = 10;
constexpr size_t kNumExtraServerThreads = 4;
- constexpr size_t kSleepMs = 50;
// make sure calls to the same object happen on the same thread
auto proc = createRpcTestSocketServerProcess({.numThreads = 1 + kNumExtraServerThreads});
- EXPECT_OK(proc.rootIface->lock());
-
- size_t epochMsBefore = epochMillis();
-
- // all these *Async commands should be queued on the server sequentially,
+ // all these *Oneway commands should be queued on the server sequentially,
// even though there are multiple threads.
- for (size_t i = 0; i + 1 < kNumSleeps; i++) {
- proc.rootIface->sleepMsAsync(kSleepMs);
+ for (size_t i = 0; i + 1 < kNumQueued; i++) {
+ proc.rootIface->blockingSendIntOneway(i);
}
- EXPECT_OK(proc.rootIface->unlockInMsAsync(kSleepMs));
-
- // this can only return once the final async call has unlocked
- EXPECT_OK(proc.rootIface->lockUnlock());
-
- size_t epochMsAfter = epochMillis();
-
- EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+ for (size_t i = 0; i + 1 < kNumQueued; i++) {
+ int n;
+ proc.rootIface->blockingRecvInt(&n);
+ EXPECT_EQ(n, i);
+ }
saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
}
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index a467ee3..d129661 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -445,6 +445,12 @@
Status blockingRecvFd(android::os::ParcelFileDescriptor* /*fd*/) override {
return Status::fromStatusT(UNKNOWN_TRANSACTION);
}
+
+ Status blockingSendIntOneway(int /*n*/) override {
+ return Status::fromStatusT(UNKNOWN_TRANSACTION);
+ }
+
+ Status blockingRecvInt(int* /*n*/) override { return Status::fromStatusT(UNKNOWN_TRANSACTION); }
};
} // namespace android
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index a27bd2f..ca5a117 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -83,6 +83,18 @@
fd->reset(mFdChannel.read());
return Status::ok();
}
+
+ HandoffChannel<int> mIntChannel;
+
+ Status blockingSendIntOneway(int n) override {
+ mIntChannel.write(n);
+ return Status::ok();
+ }
+
+ Status blockingRecvInt(int* n) override {
+ *n = mIntChannel.read();
+ return Status::ok();
+ }
};
int main(int argc, char* argv[]) {
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index 84abbac..63b56a3 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -45,9 +45,13 @@
std::to_string(serverVersion);
if (singleThreaded) {
ret += "_single_threaded";
+ } else {
+ ret += "_multi_threaded";
}
if (noKernel) {
ret += "_no_kernel";
+ } else {
+ ret += "_with_kernel";
}
return ret;
}
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index c010a2e..09422d3 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -10,9 +10,6 @@
cc_test {
name: "ftl_test",
test_suites: ["device-tests"],
- sanitize: {
- address: true,
- },
srcs: [
"cast_test.cpp",
"concat_test.cpp",
diff --git a/libs/ftl/TEST_MAPPING b/libs/ftl/TEST_MAPPING
new file mode 100644
index 0000000..ec0c671
--- /dev/null
+++ b/libs/ftl/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "ftl_test"
+ }
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "ftl_test"
+ }
+ ]
+}
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 1bfe462..198908d 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -16,17 +16,59 @@
#define LOG_TAG "Surface"
-#include <gui/view/Surface.h>
-
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
#include <binder/Parcel.h>
-
-#include <utils/Log.h>
-
#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+#include <system/window.h>
+#include <utils/Log.h>
namespace android {
namespace view {
+// Since this is a parcelable utility and we want to keep the wire format stable, only build this
+// when building the system libgui to detect any issues loading the wrong libgui from
+// libnativewindow
+
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__))
+
+extern "C" status_t android_view_Surface_writeToParcel(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel) {
+ int value;
+ int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
+ if (err != OK || value != NATIVE_WINDOW_SURFACE) {
+ ALOGE("Error: ANativeWindow is not backed by Surface");
+ return STATUS_BAD_VALUE;
+ }
+ // Use a android::view::Surface to parcelize the window
+ android::view::Surface shimSurface;
+ shimSurface.graphicBufferProducer = android::Surface::getIGraphicBufferProducer(window);
+ shimSurface.surfaceControlHandle = android::Surface::getSurfaceControlHandle(window);
+ return shimSurface.writeToParcel(parcel);
+}
+
+extern "C" status_t android_view_Surface_readFromParcel(
+ const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+ // Use a android::view::Surface to unparcel the window
+ android::view::Surface shimSurface;
+ status_t ret = shimSurface.readFromParcel(parcel);
+ if (ret != OK) {
+ ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
+ return STATUS_BAD_VALUE;
+ }
+ auto surface = sp<android::Surface>::make(shimSurface.graphicBufferProducer, false,
+ shimSurface.surfaceControlHandle);
+ ANativeWindow* anw = surface.get();
+ ANativeWindow_acquire(anw);
+ *outWindow = anw;
+ return STATUS_OK;
+}
+
+#endif
+
status_t Surface::writeToParcel(Parcel* parcel) const {
return writeToParcel(parcel, false);
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 2b7483d..2581668 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -137,10 +137,23 @@
// --- InputEvent ---
+// Due to precision limitations when working with floating points, transforming - namely
+// scaling - floating points can lead to minute errors. We round transformed values to approximately
+// three decimal places so that values like 0.99997 show up as 1.0.
+inline float roundTransformedCoords(float val) {
+ // Use a power to two to approximate three decimal places to potentially reduce some cycles.
+ // This should be at least as precise as MotionEvent::ROUNDING_PRECISION.
+ return std::round(val * 1024.f) / 1024.f;
+}
+
+inline vec2 roundTransformedCoords(vec2 p) {
+ return {roundTransformedCoords(p.x), roundTransformedCoords(p.y)};
+}
+
vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
const vec2 transformedXy = transform.transform(xy);
const vec2 transformedOrigin = transform.transform(0, 0);
- return transformedXy - transformedOrigin;
+ return roundTransformedCoords(transformedXy - transformedOrigin);
}
const char* inputEventTypeToString(int32_t type) {
@@ -548,12 +561,12 @@
float MotionEvent::getXCursorPosition() const {
vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
- return vals.x;
+ return roundTransformedCoords(vals.x);
}
float MotionEvent::getYCursorPosition() const {
vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
- return vals.y;
+ return roundTransformedCoords(vals.y);
}
void MotionEvent::setCursorPosition(float x, float y) {
@@ -875,7 +888,7 @@
static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
const vec2& xy) {
return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
- : transform.transform(xy);
+ : roundTransformedCoords(transform.transform(xy));
}
vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 4b31246..597b389 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -29,6 +29,8 @@
// Default display id.
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
class BaseTest : public testing::Test {
protected:
static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
@@ -231,95 +233,107 @@
static constexpr float RAW_X_OFFSET = 12;
static constexpr float RAW_Y_OFFSET = -41.1;
+ void SetUp() override;
+
int32_t mId;
ui::Transform mTransform;
ui::Transform mRawTransform;
+ PointerProperties mPointerProperties[2];
+ struct Sample {
+ PointerCoords pointerCoords[2];
+ };
+ std::array<Sample, 3> mSamples{};
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
+void MotionEventTest::SetUp() {
mId = InputEvent::nextId();
mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1});
- PointerProperties pointerProperties[2];
- pointerProperties[0].clear();
- pointerProperties[0].id = 1;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- pointerProperties[1].clear();
- pointerProperties[1].id = 2;
- pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mPointerProperties[0].clear();
+ mPointerProperties[0].id = 1;
+ mPointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerProperties[1].clear();
+ mPointerProperties[1].id = 2;
+ mPointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
- PointerCoords pointerCoords[2];
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
- pointerCoords[1].clear();
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
+ mSamples[0].pointerCoords[0].clear();
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17);
+ mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18);
+ mSamples[0].pointerCoords[1].clear();
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
+ mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
+
+ mSamples[1].pointerCoords[0].clear();
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
+ mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
+ mSamples[1].pointerCoords[1].clear();
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
+ mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
+
+ mSamples[2].pointerCoords[0].clear();
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
+ mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
+ mSamples[2].pointerCoords[1].clear();
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
+ mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
+}
+
+void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
- pointerProperties, pointerCoords);
-
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
- event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
-
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
- pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
- event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
+ mPointerProperties, mSamples[0].pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords);
+ event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords);
}
void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
@@ -356,51 +370,65 @@
ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
- ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ // Ensure the underlying PointerCoords are identical.
+ for (int sampleIdx = 0; sampleIdx < 3; sampleIdx++) {
+ for (int pointerIdx = 0; pointerIdx < 2; pointerIdx++) {
+ ASSERT_EQ(mSamples[sampleIdx].pointerCoords[pointerIdx],
+ event->getSamplePointerCoords()[sampleIdx * 2 + pointerIdx]);
+ }
+ }
- ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
- event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+ ASSERT_NEAR(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ EPSILON);
+ ASSERT_NEAR(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON);
+ ASSERT_NEAR(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON);
- ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0));
- ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0));
- ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1));
- ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1));
- ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0));
- ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1));
+ ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0),
+ EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1),
+ EPSILON);
- ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0));
- ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1));
- ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0));
- ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1));
+ ASSERT_NEAR(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0), EPSILON);
+ ASSERT_NEAR(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1), EPSILON);
- ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
- ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
+ ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0), EPSILON);
+ ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1), EPSILON);
- ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
- ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
+ ASSERT_NEAR(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 210 * X_SCALE, event->getX(0), EPSILON);
+ ASSERT_NEAR(X_OFFSET + 220 * X_SCALE, event->getX(1), EPSILON);
+
+ ASSERT_NEAR(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 211 * Y_SCALE, event->getY(0), EPSILON);
+ ASSERT_NEAR(Y_OFFSET + 221 * Y_SCALE, event->getY(1), EPSILON);
ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -532,10 +560,10 @@
ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
- ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0));
- ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0));
- ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
- ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
+ ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON);
+ ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON);
+ ASSERT_NEAR((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0), EPSILON);
+ ASSERT_NEAR((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0), EPSILON);
ASSERT_EQ(212, event.getPressure(0));
ASSERT_EQ(213, event.getSize(0));
ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -773,18 +801,18 @@
// The x and y axes should have the window transform applied.
const auto newPoint = transform.transform(60, 100);
- ASSERT_EQ(newPoint.x, event.getX(0));
- ASSERT_EQ(newPoint.y, event.getY(0));
+ ASSERT_NEAR(newPoint.x, event.getX(0), EPSILON);
+ ASSERT_NEAR(newPoint.y, event.getY(0), EPSILON);
// The raw values should have the display transform applied.
const auto raw = rawTransform.transform(60, 100);
- ASSERT_EQ(raw.x, event.getRawX(0));
- ASSERT_EQ(raw.y, event.getRawY(0));
+ ASSERT_NEAR(raw.x, event.getRawX(0), EPSILON);
+ ASSERT_NEAR(raw.y, event.getRawY(0), EPSILON);
// Relative values should have the window transform applied without any translation.
const auto rel = transformWithoutTranslation(transform, 42, 96);
- ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
+ ASSERT_NEAR(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), EPSILON);
+ ASSERT_NEAR(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), EPSILON);
}
TEST_F(MotionEventTest, Initialize_SetsClassification) {
@@ -851,4 +879,42 @@
ASSERT_EQ(4, event.getYCursorPosition());
}
+TEST_F(MotionEventTest, CoordinatesAreRoundedAppropriately) {
+ // These are specifically integral values, since we are testing for rounding.
+ const vec2 EXPECTED{400.f, 700.f};
+
+ // Pick a transform such that transforming the point with its inverse and bringing that
+ // back to the original coordinate space results in a non-zero error amount due to the
+ // nature of floating point arithmetics. This can happen when the display is scaled.
+ // For example, the 'adb shell wm size' command can be used to set an override for the
+ // logical display size, which could result in the display being scaled.
+ constexpr float scale = 720.f / 1080.f;
+ ui::Transform transform;
+ transform.set(scale, 0, 0, scale);
+ ASSERT_NE(EXPECTED, transform.transform(transform.inverse().transform(EXPECTED)));
+
+ // Store the inverse-transformed values in the motion event.
+ const vec2 rawCoords = transform.inverse().transform(EXPECTED);
+ PointerCoords pc{};
+ pc.setAxisValue(AMOTION_EVENT_AXIS_X, rawCoords.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_Y, rawCoords.y);
+ PointerProperties pp{};
+ MotionEvent event;
+ event.initialize(InputEvent::nextId(), 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
+ AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
+ AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
+ MotionClassification::NONE, transform, 2.0f, 2.1f, rawCoords.x, rawCoords.y,
+ transform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 1, &pp, &pc);
+
+ // When using the getters from the MotionEvent to obtain the coordinates, the transformed
+ // values should be rounded by an appropriate amount so that they now precisely equal the
+ // original coordinates.
+ ASSERT_EQ(EXPECTED.x, event.getX(0));
+ ASSERT_EQ(EXPECTED.y, event.getY(0));
+ ASSERT_EQ(EXPECTED.x, event.getRawX(0));
+ ASSERT_EQ(EXPECTED.y, event.getRawY(0));
+ ASSERT_EQ(EXPECTED.x, event.getXCursorPosition());
+ ASSERT_EQ(EXPECTED.y, event.getYCursorPosition());
+}
+
} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 05bc0bc..8393e99 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -32,6 +32,8 @@
namespace android {
+constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
class InputPublisherAndConsumerTest : public testing::Test {
protected:
std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
@@ -233,10 +235,10 @@
EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
- EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
- EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
- EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
- EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
+ EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON);
+ EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON);
+ EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON);
+ EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON);
EXPECT_EQ(rawTransform, motionEvent->getRawTransform());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
@@ -249,10 +251,12 @@
EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
const auto& pc = pointerCoords[i];
- EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i));
- EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i));
- EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i));
- EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i));
+ EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]);
+
+ EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON);
+ EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON);
+ EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON);
+ EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON);
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i));
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i));
EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i));
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 3762e66..73a05fc 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -24,14 +24,49 @@
#include <private/android/AHardwareBufferHelpers.h>
+#include <android/binder_libbinder.h>
+#include <dlfcn.h>
#include <log/log.h>
#include <ui/GraphicBuffer.h>
-#include <gui/Surface.h>
-#include <gui/view/Surface.h>
-#include <android/binder_libbinder.h>
using namespace android;
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error libnativewindow can only be built for system
+#endif
+
+using android_view_Surface_writeToParcel = status_t (*)(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel);
+
+using android_view_Surface_readFromParcel =
+ status_t (*)(const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow);
+
+struct SurfaceParcelables {
+ android_view_Surface_writeToParcel write = nullptr;
+ android_view_Surface_readFromParcel read = nullptr;
+};
+
+const SurfaceParcelables* getSurfaceParcelFunctions() {
+ static SurfaceParcelables funcs = []() -> SurfaceParcelables {
+ SurfaceParcelables ret;
+ void* dl = dlopen("libgui.so", RTLD_NOW);
+ LOG_ALWAYS_FATAL_IF(!dl, "Failed to find libgui.so");
+ ret.write =
+ (android_view_Surface_writeToParcel)dlsym(dl, "android_view_Surface_writeToParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.write,
+ "libgui.so missing android_view_Surface_writeToParcel; "
+ "loaded wrong libgui?");
+ ret.read =
+ (android_view_Surface_readFromParcel)dlsym(dl,
+ "android_view_Surface_readFromParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.read,
+ "libgui.so missing android_view_Surface_readFromParcel; "
+ "loaded wrong libgui?");
+ return ret;
+ }();
+ return &funcs;
+}
+
static int32_t query(ANativeWindow* window, int what) {
int value;
int res = window->query(window, what, &value);
@@ -64,13 +99,6 @@
return false;
}
}
-static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) {
- return Surface::getIGraphicBufferProducer(window);
-}
-
-static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) {
- return Surface::getSurfaceControlHandle(window);
-}
/**************************************************************************************************
* NDK
@@ -348,38 +376,24 @@
binder_status_t ANativeWindow_readFromParcel(
const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
- const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
-
- // Use a android::view::Surface to unparcel the window
- std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
- status_t ret = shimSurface->readFromParcel(nativeParcel);
- if (ret != OK) {
- ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
- return STATUS_BAD_VALUE;
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->read == nullptr) {
+ ALOGE("Failed to load Surface_readFromParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
}
- sp<Surface> surface = sp<Surface>::make(
- shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle);
- ANativeWindow* anw = surface.get();
- ANativeWindow_acquire(anw);
- *outWindow = anw;
- return STATUS_OK;
+ const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+ return funcs->read(nativeParcel, outWindow);
}
binder_status_t ANativeWindow_writeToParcel(
ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) {
- int value;
- int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
- if (err != OK || value != NATIVE_WINDOW_SURFACE) {
- ALOGE("Error: ANativeWindow is not backed by Surface");
- return STATUS_BAD_VALUE;
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->write == nullptr) {
+ ALOGE("Failed to load Surface_writeToParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
}
- // Use a android::view::Surface to parcelize the window
- std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
- shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window);
- shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window);
-
Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
- return shimSurface->writeToParcel(nativeParcel);
+ return funcs->write(window, nativeParcel);
}
/**************************************************************************************************
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index eb97a68..96164c0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -107,6 +107,8 @@
constexpr int LOGTAG_INPUT_FOCUS = 62001;
constexpr int LOGTAG_INPUT_CANCEL = 62003;
+const ui::Transform kIdentityTransform;
+
inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
@@ -459,8 +461,8 @@
}
// Returns true if the given window can accept pointer events at the given display location.
-bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y,
- bool isStylus) {
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y,
+ bool isStylus, const ui::Transform& displayTransform) {
const auto inputConfig = windowInfo.inputConfig;
if (windowInfo.displayId != displayId ||
inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
@@ -470,7 +472,17 @@
if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
return false;
}
- if (!windowInfo.touchableRegionContainsPoint(x, y)) {
+
+ // Window Manager works in the logical display coordinate space. When it specifies bounds for a
+ // window as (l, t, r, b), the range of x in [l, r) and y in [t, b) are considered to be inside
+ // the window. Points on the right and bottom edges should not be inside the window, so we need
+ // to be careful about performing a hit test when the display is rotated, since the "right" and
+ // "bottom" of the window will be different in the display (un-rotated) space compared to in the
+ // logical display in which WM determined the bounds. Perform the hit test in the logical
+ // display space to ensure these edges are considered correctly in all orientations.
+ const auto touchableRegion = displayTransform.transform(windowInfo.touchableRegion);
+ const auto p = displayTransform.transform(x, y);
+ if (!touchableRegion.contains(std::floor(p.x), std::floor(p.y))) {
return false;
}
return true;
@@ -928,10 +940,8 @@
// the touch into the other window.
if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
int32_t displayId = motionEntry.displayId;
- int32_t x = static_cast<int32_t>(
- motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = static_cast<int32_t>(
- motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ const float x = motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
sp<WindowInfoHandle> touchedWindowHandle =
@@ -1058,8 +1068,8 @@
}
}
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
- int32_t y, TouchState* touchState,
+sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y,
+ TouchState* touchState,
bool isStylus,
bool addOutsideTargets,
bool ignoreDragWindow) {
@@ -1074,7 +1084,8 @@
}
const WindowInfo& info = *windowHandle->getInfo();
- if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+ if (!info.isSpy() &&
+ windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
return windowHandle;
}
@@ -1088,14 +1099,14 @@
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- int32_t displayId, int32_t x, int32_t y, bool isStylus) const {
+ int32_t displayId, float x, float y, bool isStylus) const {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
const WindowInfo& info = *windowHandle->getInfo();
- if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
+ if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
continue;
}
if (!info.isSpy()) {
@@ -2057,16 +2068,16 @@
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
- int32_t x;
- int32_t y;
+ float x;
+ float y;
const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
// Always dispatch mouse events to cursor position.
if (isFromMouse) {
- x = int32_t(entry.xCursorPosition);
- y = int32_t(entry.yCursorPosition);
+ x = entry.xCursorPosition;
+ y = entry.yCursorPosition;
} else {
- x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
- y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ x = entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X);
+ y = entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y);
}
const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2075,8 +2086,7 @@
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
- ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
- displayId);
+ ALOGD("No new touched window at (%.1f, %.1f) in display %" PRId32, x, y, displayId);
// Try to assign the pointer to the first foreground window we find, if there is one.
newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
@@ -2123,7 +2133,8 @@
}
if (newTouchedWindows.empty()) {
- ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
+ ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display "
+ "%d.",
x, y, displayId);
injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
@@ -2157,7 +2168,8 @@
computeTouchOcclusionInfoLocked(windowHandle, x, y);
if (!isTouchTrustedLocked(occlusionInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
- ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+ ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x,
+ y);
for (const auto& log : occlusionInfo.debugInfo) {
ALOGD("%s", log.c_str());
}
@@ -2218,8 +2230,8 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
- const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ const float x = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
sp<WindowInfoHandle> oldTouchedWindowHandle =
@@ -2541,8 +2553,8 @@
}
const int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
- const int32_t x = entry.pointerCoords[pointerIndex].getX();
- const int32_t y = entry.pointerCoords[pointerIndex].getY();
+ const float x = entry.pointerCoords[pointerIndex].getX();
+ const float y = entry.pointerCoords[pointerIndex].getY();
switch (maskedAction) {
case AMOTION_EVENT_ACTION_MOVE: {
@@ -4582,6 +4594,12 @@
return getWindowHandleLocked(focusedToken, displayId);
}
+ui::Transform InputDispatcher::getTransformLocked(int32_t displayId) const {
+ auto displayInfoIt = mDisplayInfos.find(displayId);
+ return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform
+ : kIdentityTransform;
+}
+
bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
const bool noInputChannel =
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ed89ed0..24e7432 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -240,11 +240,11 @@
std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
- int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
+ int32_t displayId, float x, float y, TouchState* touchState, bool isStylus = false,
bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
- int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
+ int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
REQUIRES(mLock);
@@ -370,6 +370,7 @@
int32_t displayId) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
const sp<IBinder>& windowHandleToken) const REQUIRES(mLock);
+ ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock);
// Same function as above, but faster. Since displayId is provided, this avoids the need
// to loop through all displays.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index fce0f99..e860e3c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -128,10 +128,10 @@
const auto& motionEvent = static_cast<const MotionEvent&>(event);
EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
EXPECT_EQ(motionEvent.getAction(), args.action);
- EXPECT_EQ(motionEvent.getX(0), point.x);
- EXPECT_EQ(motionEvent.getY(0), point.y);
- EXPECT_EQ(motionEvent.getRawX(0), point.x);
- EXPECT_EQ(motionEvent.getRawY(0), point.y);
+ EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION);
});
}
@@ -2481,8 +2481,7 @@
public:
void SetUp() override {
InputDispatcherTest::SetUp();
- mDisplayInfos.clear();
- mWindowInfos.clear();
+ removeAllWindowsAndDisplays();
}
void addDisplayInfo(int displayId, const ui::Transform& transform) {
@@ -2498,6 +2497,11 @@
mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
}
+ void removeAllWindowsAndDisplays() {
+ mDisplayInfos.clear();
+ mWindowInfos.clear();
+ }
+
// Set up a test scenario where the display has a scaled projection and there are two windows
// on the display.
std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() {
@@ -2529,11 +2533,11 @@
std::vector<gui::WindowInfo> mWindowInfos;
};
-TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) {
+TEST_F(InputDispatcherDisplayProjectionTest, HitTestCoordinateSpaceConsistency) {
auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
// Send down to the first window. The point is represented in the display space. The point is
- // selected so that if the hit test was done with the transform applied to it, then it would
- // end up in the incorrect window.
+ // selected so that if the hit test was performed with the point and the bounds being in
+ // different coordinate spaces, the event would end up in the incorrect window.
NotifyMotionArgs downMotionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {PointF{75, 55}});
@@ -2608,6 +2612,78 @@
EXPECT_EQ(80, event->getY(0));
}
+/** Ensure consistent behavior of InputDispatcher in all orientations. */
+class InputDispatcherDisplayOrientationFixture
+ : public InputDispatcherDisplayProjectionTest,
+ public ::testing::WithParamInterface<ui::Rotation> {};
+
+// This test verifies the touchable region of a window for all rotations of the display by tapping
+// in different locations on the display, specifically points close to the four corners of a
+// window.
+TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) {
+ constexpr static int32_t displayWidth = 400;
+ constexpr static int32_t displayHeight = 800;
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ const auto rotation = GetParam();
+
+ // Set up the display with the specified rotation.
+ const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270;
+ const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth;
+ const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight;
+ const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation),
+ logicalDisplayWidth, logicalDisplayHeight);
+ addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+
+ // Create a window with its bounds determined in the logical display.
+ const Rect frameInLogicalDisplay(100, 100, 200, 300);
+ const Rect frameInDisplay = displayTransform.inverse().transform(frameInLogicalDisplay);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(frameInDisplay, displayTransform);
+ addWindow(window);
+
+ // The following points in logical display space should be inside the window.
+ static const std::array<vec2, 4> insidePoints{
+ {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}};
+ for (const auto pointInsideWindow : insidePoints) {
+ const vec2 p = displayTransform.inverse().transform(pointInsideWindow);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&down);
+ window->consumeMotionDown();
+
+ const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&up);
+ window->consumeMotionUp();
+ }
+
+ // The following points in logical display space should be outside the window.
+ static const std::array<vec2, 5> outsidePoints{
+ {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}};
+ for (const auto pointOutsideWindow : outsidePoints) {
+ const vec2 p = displayTransform.inverse().transform(pointOutsideWindow);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&down);
+
+ const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {pointInDisplaySpace});
+ mDispatcher->notifyMotion(&up);
+ }
+ window->assertNoEvents();
+}
+
+// Run the precision tests for all rotations.
+INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests,
+ InputDispatcherDisplayOrientationFixture,
+ ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
+ ui::ROTATION_270));
+
using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher,
sp<IBinder>, sp<IBinder>)>;