Merge "[RE-SkiaVk] Update VK_ERROR_DEVICE_LOST logging to use stringstream" into main
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index a401838..adcef91 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -150,7 +150,7 @@
std::optional<std::string> updatableViaApex;
forEachManifest([&](const ManifestWithDescription& mwd) {
- mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
+ bool cont = mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
if (manifestInstance.format() != vintf::HalFormat::AIDL) return true;
if (manifestInstance.package() != aname.package) return true;
if (manifestInstance.interface() != aname.iface) return true;
@@ -158,8 +158,7 @@
updatableViaApex = manifestInstance.updatableViaApex();
return false; // break (libvintf uses opposite convention)
});
- if (updatableViaApex.has_value()) return true; // break (found match)
- return false; // continue
+ return !cont;
});
return updatableViaApex;
diff --git a/data/etc/android.hardware.location.gps.xml b/data/etc/android.hardware.location.gps.xml
index 72ab732..2a55370 100644
--- a/data/etc/android.hardware.location.gps.xml
+++ b/data/etc/android.hardware.location.gps.xml
@@ -17,6 +17,5 @@
<!-- These are the location-related features for devices that include GPS. -->
<permissions>
<feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
<feature name="android.hardware.location.gps" />
</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 95b8110..beb69f8 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -28,7 +28,6 @@
<feature name="android.hardware.audio.output" />
<feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
<feature name="android.hardware.bluetooth" />
<feature name="android.hardware.touchscreen" />
<feature name="android.hardware.microphone" />
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index f8fb256..79cdbca 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -839,6 +839,10 @@
AKEYCODE_MACRO_3 = 315,
/** User customizable key #4. */
AKEYCODE_MACRO_4 = 316,
+ /** Open Emoji picker */
+ AKEYCODE_EMOJI_PICKER = 317,
+ /** Take Screenshot */
+ AKEYCODE_SCREENSHOT = 318,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/ftl/details/future.h b/include/ftl/details/future.h
index df1323e..8d82e0f 100644
--- a/include/ftl/details/future.h
+++ b/include/ftl/details/future.h
@@ -73,8 +73,18 @@
return std::get<Impl>(self()).get();
}
+ template <class Rep, class Period>
+ std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
+ if (std::holds_alternative<T>(self())) {
+ return std::future_status::ready;
+ }
+
+ return std::get<Impl>(self()).wait_for(timeout_duration);
+ }
+
private:
auto& self() { return static_cast<Self&>(*this).future_; }
+ const auto& self() const { return static_cast<const Self&>(*this).future_; }
};
template <typename Self, typename T>
@@ -90,6 +100,15 @@
return std::get<Impl>(self()).get();
}
+ template <class Rep, class Period>
+ std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
+ if (std::holds_alternative<T>(self())) {
+ return std::future_status::ready;
+ }
+
+ return std::get<Impl>(self()).wait_for(timeout_duration);
+ }
+
private:
const auto& self() const { return static_cast<const Self&>(*this).future_; }
};
diff --git a/include/ftl/expected.h b/include/ftl/expected.h
new file mode 100644
index 0000000..12b6102
--- /dev/null
+++ b/include/ftl/expected.h
@@ -0,0 +1,65 @@
+/*
+ * 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 <android-base/expected.h>
+#include <ftl/optional.h>
+
+#include <utility>
+
+namespace android::ftl {
+
+// Superset of base::expected<T, E> with monadic operations.
+//
+// TODO: Extend std::expected<T, E> in C++23.
+//
+template <typename T, typename E>
+struct Expected final : base::expected<T, E> {
+ using Base = base::expected<T, E>;
+ using Base::expected;
+
+ using Base::error;
+ using Base::has_value;
+ using Base::value;
+
+ template <typename P>
+ constexpr bool has_error(P predicate) const {
+ return !has_value() && predicate(error());
+ }
+
+ constexpr Optional<T> value_opt() const& {
+ return has_value() ? Optional(value()) : std::nullopt;
+ }
+
+ constexpr Optional<T> value_opt() && {
+ return has_value() ? Optional(std::move(value())) : std::nullopt;
+ }
+
+ // Delete new for this class. Its base doesn't have a virtual destructor, and
+ // if it got deleted via base class pointer, it would cause undefined
+ // behavior. There's not a good reason to allocate this object on the heap
+ // anyway.
+ static void* operator new(size_t) = delete;
+ static void* operator new[](size_t) = delete;
+};
+
+template <typename E>
+constexpr auto Unexpected(E&& error) {
+ return base::unexpected(std::forward<E>(error));
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/future.h b/include/ftl/future.h
index c78f9b7..dad180f 100644
--- a/include/ftl/future.h
+++ b/include/ftl/future.h
@@ -51,6 +51,7 @@
// Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the
// following are defined for either FutureImpl:
using Base::get;
+ using Base::wait_for;
// Attaches a continuation to the future. The continuation is a function that maps T to either R
// or ftl::Future<R>. In the former case, the chain wraps the result in a future as if by
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 2d64872..8b1c2a3 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -35,18 +35,16 @@
#include <android-base/result.h>
#include <android-base/unique_fd.h>
+#include <android/os/InputChannelCore.h>
#include <binder/IBinder.h>
-#include <binder/Parcelable.h>
#include <input/Input.h>
#include <input/InputVerifier.h>
#include <sys/stat.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
-#include <utils/RefBase.h>
#include <utils/Timers.h>
-
namespace android {
class Parcel;
@@ -231,18 +229,15 @@
* input messages across processes. Each channel has a descriptive name for debugging purposes.
*
* Each endpoint has its own InputChannel object that specifies its file descriptor.
+ * For parceling, this relies on android::os::InputChannelCore, defined in aidl.
*
* The input channel is closed when all references to it are released.
*/
-class InputChannel : public Parcelable {
+class InputChannel : private android::os::InputChannelCore {
public:
- static std::unique_ptr<InputChannel> create(const std::string& name,
- android::base::unique_fd fd, sp<IBinder> token);
- InputChannel() = default;
- InputChannel(const InputChannel& other)
- : mName(other.mName), mFd(other.dupFd()), mToken(other.mToken){};
- InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
- ~InputChannel() override;
+ static std::unique_ptr<InputChannel> create(android::os::InputChannelCore&& parceledChannel);
+ ~InputChannel();
+
/**
* Create a pair of input channels.
* The two returned input channels are equivalent, and are labeled as "server" and "client"
@@ -254,9 +249,8 @@
std::unique_ptr<InputChannel>& outServerChannel,
std::unique_ptr<InputChannel>& outClientChannel);
- inline std::string getName() const { return mName; }
- inline const android::base::unique_fd& getFd() const { return mFd; }
- inline sp<IBinder> getToken() const { return mToken; }
+ inline std::string getName() const { return name; }
+ inline int getFd() const { return fd.get(); }
/* Send a message to the other endpoint.
*
@@ -304,10 +298,7 @@
/* Return a new object that has a duplicate of this channel's fd. */
std::unique_ptr<InputChannel> dup() const;
- void copyTo(InputChannel& outChannel) const;
-
- status_t readFromParcel(const android::Parcel* parcel) override;
- status_t writeToParcel(android::Parcel* parcel) const override;
+ void copyTo(android::os::InputChannelCore& outChannel) const;
/**
* The connection token is used to identify the input connection, i.e.
@@ -323,26 +314,11 @@
*/
sp<IBinder> getConnectionToken() const;
- bool operator==(const InputChannel& inputChannel) const {
- struct stat lhs, rhs;
- if (fstat(mFd.get(), &lhs) != 0) {
- return false;
- }
- if (fstat(inputChannel.getFd().get(), &rhs) != 0) {
- return false;
- }
- // If file descriptors are pointing to same inode they are duplicated fds.
- return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken &&
- lhs.st_ino == rhs.st_ino;
- }
-
private:
- base::unique_fd dupFd() const;
+ static std::unique_ptr<InputChannel> create(const std::string& name,
+ android::base::unique_fd fd, sp<IBinder> token);
- std::string mName;
- base::unique_fd mFd;
-
- sp<IBinder> mToken;
+ InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
};
/*
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 918680d..5ac965f 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -10,11 +10,15 @@
cc_test {
name: "ftl_test",
test_suites: ["device-tests"],
+ header_libs: [
+ "libbase_headers",
+ ],
srcs: [
"algorithm_test.cpp",
"cast_test.cpp",
"concat_test.cpp",
"enum_test.cpp",
+ "expected_test.cpp",
"fake_guard_test.cpp",
"flags_test.cpp",
"function_test.cpp",
diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp
new file mode 100644
index 0000000..8cb07e4
--- /dev/null
+++ b/libs/ftl/expected_test.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 <ftl/expected.h>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <system_error>
+
+namespace android::test {
+
+using IntExp = ftl::Expected<int, std::errc>;
+using StringExp = ftl::Expected<std::string, std::errc>;
+
+using namespace std::string_literals;
+
+TEST(Expected, Construct) {
+ // Default value.
+ EXPECT_TRUE(IntExp().has_value());
+ EXPECT_EQ(IntExp(), IntExp(0));
+
+ EXPECT_TRUE(StringExp().has_value());
+ EXPECT_EQ(StringExp(), StringExp(""));
+
+ // Value.
+ ASSERT_TRUE(IntExp(42).has_value());
+ EXPECT_EQ(42, IntExp(42).value());
+
+ ASSERT_TRUE(StringExp("test").has_value());
+ EXPECT_EQ("test"s, StringExp("test").value());
+
+ // Error.
+ const auto exp = StringExp(ftl::Unexpected(std::errc::invalid_argument));
+ ASSERT_FALSE(exp.has_value());
+ EXPECT_EQ(std::errc::invalid_argument, exp.error());
+}
+
+TEST(Expected, HasError) {
+ EXPECT_FALSE(IntExp(123).has_error([](auto) { return true; }));
+ EXPECT_FALSE(IntExp(ftl::Unexpected(std::errc::io_error)).has_error([](auto) { return false; }));
+
+ EXPECT_TRUE(StringExp(ftl::Unexpected(std::errc::permission_denied)).has_error([](auto e) {
+ return e == std::errc::permission_denied;
+ }));
+}
+
+TEST(Expected, ValueOpt) {
+ EXPECT_EQ(ftl::Optional(-1), IntExp(-1).value_opt());
+ EXPECT_EQ(std::nullopt, IntExp(ftl::Unexpected(std::errc::broken_pipe)).value_opt());
+
+ {
+ const StringExp exp("foo"s);
+ EXPECT_EQ(ftl::Optional('f'),
+ exp.value_opt().transform([](const auto& s) { return s.front(); }));
+ EXPECT_EQ("foo"s, exp.value());
+ }
+ {
+ StringExp exp("foobar"s);
+ EXPECT_EQ(ftl::Optional(6), std::move(exp).value_opt().transform(&std::string::length));
+ EXPECT_TRUE(exp.value().empty());
+ }
+}
+
+} // namespace android::test
diff --git a/libs/ftl/future_test.cpp b/libs/ftl/future_test.cpp
index 5a245b6..1140639 100644
--- a/libs/ftl/future_test.cpp
+++ b/libs/ftl/future_test.cpp
@@ -102,4 +102,42 @@
decrement_thread.join();
}
+TEST(Future, WaitFor) {
+ using namespace std::chrono_literals;
+ {
+ auto future = ftl::yield(42);
+ // Check that we can wait_for multiple times without invalidating the future
+ EXPECT_EQ(future.wait_for(1s), std::future_status::ready);
+ EXPECT_EQ(future.wait_for(1s), std::future_status::ready);
+ EXPECT_EQ(future.get(), 42);
+ }
+
+ {
+ std::condition_variable cv;
+ std::mutex m;
+ bool ready = false;
+
+ std::packaged_task<int32_t()> get_int([&] {
+ std::unique_lock lk(m);
+ cv.wait(lk, [&] { return ready; });
+ return 24;
+ });
+
+ auto get_future = ftl::Future(get_int.get_future());
+ std::thread get_thread(std::move(get_int));
+
+ EXPECT_EQ(get_future.wait_for(0s), std::future_status::timeout);
+ {
+ std::unique_lock lk(m);
+ ready = true;
+ }
+ cv.notify_one();
+
+ EXPECT_EQ(get_future.wait_for(1s), std::future_status::ready);
+ EXPECT_EQ(get_future.get(), 24);
+
+ get_thread.join();
+ }
+}
+
} // namespace android::test
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index b9ab803..a9d6e8d 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -63,8 +63,7 @@
using Transaction = SurfaceComposerClient::Transaction;
sp<IInputFlinger> getInputFlinger() {
- sp<IBinder> input(defaultServiceManager()->getService(
- String16("inputflinger")));
+ sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else { ALOGE("Linked to input"); }
@@ -104,8 +103,13 @@
if (noInputChannel) {
mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true);
} else {
- mClientChannel = std::make_shared<InputChannel>();
- mInputFlinger->createInputChannel("testchannels", mClientChannel.get());
+ android::os::InputChannelCore tempChannel;
+ android::binder::Status result =
+ mInputFlinger->createInputChannel("testchannels", &tempChannel);
+ if (!result.isOk()) {
+ ADD_FAILURE() << "binder call to createInputChannel failed";
+ }
+ mClientChannel = InputChannel::create(std::move(tempChannel));
mInputInfo.token = mClientChannel->getConnectionToken();
mInputConsumer = new InputConsumer(mClientChannel);
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 3d3bf13..e5fb2c5 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -175,6 +175,7 @@
],
srcs: [
"android/os/IInputFlinger.aidl",
+ "android/os/InputChannelCore.aidl",
"AccelerationCurve.cpp",
"Input.cpp",
"InputDevice.cpp",
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 0e627e5..8db0ca5 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -348,7 +348,9 @@
DEFINE_KEYCODE(MACRO_1), \
DEFINE_KEYCODE(MACRO_2), \
DEFINE_KEYCODE(MACRO_3), \
- DEFINE_KEYCODE(MACRO_4)
+ DEFINE_KEYCODE(MACRO_4), \
+ DEFINE_KEYCODE(EMOJI_PICKER), \
+ DEFINE_KEYCODE(SCREENSHOT)
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 37de00c..0e0e80d 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -95,6 +95,21 @@
return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
}
+android::base::unique_fd dupChannelFd(int fd) {
+ android::base::unique_fd newFd(::dup(fd));
+ if (!newFd.ok()) {
+ ALOGE("Could not duplicate fd %i : %s", fd, strerror(errno));
+ const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
+ // If this process is out of file descriptors, then throwing that might end up exploding
+ // on the other side of a binder call, which isn't really helpful.
+ // Better to just crash here and hope that the FD leak is slow.
+ // Other failures could be client errors, so we still propagate those back to the caller.
+ LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel");
+ return {};
+ }
+ return newFd;
+}
+
} // namespace
using android::base::Result;
@@ -395,15 +410,23 @@
return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token));
}
-InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token)
- : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {
+std::unique_ptr<InputChannel> InputChannel::create(
+ android::os::InputChannelCore&& parceledChannel) {
+ return InputChannel::create(parceledChannel.name, parceledChannel.fd.release(),
+ parceledChannel.token);
+}
+
+InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) {
+ this->name = std::move(name);
+ this->fd.reset(std::move(fd));
+ this->token = std::move(token);
ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel constructed: name='%s', fd=%d",
- getName().c_str(), getFd().get());
+ getName().c_str(), getFd());
}
InputChannel::~InputChannel() {
ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel destroyed: name='%s', fd=%d",
- getName().c_str(), getFd().get());
+ getName().c_str(), getFd());
}
status_t InputChannel::openInputChannelPair(const std::string& name,
@@ -441,19 +464,19 @@
ATRACE_NAME_IF(ATRACE_ENABLED(),
StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32
")",
- mName.c_str(), msg->header.seq, msg->header.type));
+ name.c_str(), msg->header.seq, msg->header.type));
const size_t msgLength = msg->size();
InputMessage cleanMsg;
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
- nWrite = ::send(getFd().get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+ nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
int error = errno;
ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ error sending message of type %s, %s",
- mName.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error));
+ name.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error));
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
@@ -465,12 +488,12 @@
if (size_t(nWrite) != msgLength) {
ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
- "channel '%s' ~ error sending message type %s, send was incomplete", mName.c_str(),
+ "channel '%s' ~ error sending message type %s, send was incomplete", name.c_str(),
ftl::enum_string(msg->header.type).c_str());
return DEAD_OBJECT;
}
- ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(),
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", name.c_str(),
ftl::enum_string(msg->header.type).c_str());
return OK;
@@ -479,13 +502,13 @@
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
- nRead = ::recv(getFd().get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
+ nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
int error = errno;
ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d",
- mName.c_str(), errno);
+ name.c_str(), errno);
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
@@ -497,29 +520,29 @@
if (nRead == 0) { // check for EOF
ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
- "channel '%s' ~ receive message failed because peer was closed", mName.c_str());
+ "channel '%s' ~ receive message failed because peer was closed", name.c_str());
return DEAD_OBJECT;
}
if (!msg->isValid(nRead)) {
- ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead);
+ ALOGE("channel '%s' ~ received invalid message of size %zd", name.c_str(), nRead);
return BAD_VALUE;
}
- ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(),
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", name.c_str(),
ftl::enum_string(msg->header.type).c_str());
if (ATRACE_ENABLED()) {
// Add an additional trace point to include data about the received message.
std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32
", type=0x%" PRIx32 ")",
- mName.c_str(), msg->header.seq, msg->header.type);
+ name.c_str(), msg->header.seq, msg->header.type);
ATRACE_NAME(message.c_str());
}
return OK;
}
bool InputChannel::probablyHasInput() const {
- struct pollfd pfds = {.fd = mFd, .events = POLLIN};
+ struct pollfd pfds = {.fd = fd.get(), .events = POLLIN};
if (::poll(&pfds, /*nfds=*/1, /*timeout=*/0) <= 0) {
// This can be a false negative because EINTR and ENOMEM are not handled. The latter should
// be extremely rare. The EINTR is also unlikely because it happens only when the signal
@@ -538,7 +561,7 @@
if (timeout < 0ms) {
LOG(FATAL) << "Timeout cannot be negative, received " << timeout.count();
}
- struct pollfd pfds = {.fd = mFd, .events = POLLIN};
+ struct pollfd pfds = {.fd = fd.get(), .events = POLLIN};
int ret;
std::chrono::time_point<std::chrono::steady_clock> stopTime =
std::chrono::steady_clock::now() + timeout;
@@ -551,53 +574,18 @@
}
std::unique_ptr<InputChannel> InputChannel::dup() const {
- base::unique_fd newFd(dupFd());
+ base::unique_fd newFd(dupChannelFd(fd.get()));
return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
}
-void InputChannel::copyTo(InputChannel& outChannel) const {
- outChannel.mName = getName();
- outChannel.mFd = dupFd();
- outChannel.mToken = getConnectionToken();
-}
-
-status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
- if (parcel == nullptr) {
- ALOGE("%s: Null parcel", __func__);
- return BAD_VALUE;
- }
- return parcel->writeStrongBinder(mToken)
- ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd);
-}
-
-status_t InputChannel::readFromParcel(const android::Parcel* parcel) {
- if (parcel == nullptr) {
- ALOGE("%s: Null parcel", __func__);
- return BAD_VALUE;
- }
- mToken = parcel->readStrongBinder();
- return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd);
+void InputChannel::copyTo(android::os::InputChannelCore& outChannel) const {
+ outChannel.name = getName();
+ outChannel.fd.reset(dupChannelFd(fd.get()));
+ outChannel.token = getConnectionToken();
}
sp<IBinder> InputChannel::getConnectionToken() const {
- return mToken;
-}
-
-base::unique_fd InputChannel::dupFd() const {
- base::unique_fd newFd(::dup(getFd().get()));
- if (!newFd.ok()) {
- ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
- strerror(errno));
- const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
- // If this process is out of file descriptors, then throwing that might end up exploding
- // on the other side of a binder call, which isn't really helpful.
- // Better to just crash here and hope that the FD leak is slow.
- // Other failures could be client errors, so we still propagate those back to the caller.
- LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
- getName().c_str());
- return {};
- }
- return newFd;
+ return token;
}
// --- InputPublisher ---
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
index 00ebd4d..c1aacfb 100644
--- a/libs/input/android/os/IInputFlinger.aidl
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -16,14 +16,13 @@
package android.os;
-import android.InputChannel;
+import android.os.InputChannelCore;
import android.gui.FocusRequest;
-import android.gui.WindowInfo;
/** @hide */
interface IInputFlinger
{
- InputChannel createInputChannel(in @utf8InCpp String name);
+ InputChannelCore createInputChannel(in @utf8InCpp String name);
void removeInputChannel(in IBinder connectionToken);
/**
* Sets focus to the window identified by the token. This must be called
diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/os/InputChannelCore.aidl
similarity index 72%
rename from libs/input/android/InputChannel.aidl
rename to libs/input/android/os/InputChannelCore.aidl
index c2d1112..888a553 100644
--- a/libs/input/android/InputChannel.aidl
+++ b/libs/input/android/os/InputChannelCore.aidl
@@ -15,6 +15,16 @@
** limitations under the License.
*/
-package android;
+package android.os;
-parcelable InputChannel cpp_header "input/InputTransport.h";
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Input channel struct for sending InputChannel between processes.
+ * @hide
+ */
+parcelable InputChannelCore {
+ @utf8InCpp String name;
+ ParcelFileDescriptor fd;
+ IBinder token;
+}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 0c850fe..174464d 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -26,6 +26,13 @@
namespace: "input"
description: "Set to true to enable timer support for the touchpad Gestures library"
bug: "297192727"
+ }
+
+ flag {
+ name: "enable_input_event_tracing"
+ namespace: "input"
+ description: "Set to true to enable input event tracing, including always-on tracing on non-user builds"
+ bug: "210460522"
}
flag {
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index 867af79..b1d7760 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -79,7 +79,7 @@
logger::init(
logger::Config::default()
.with_tag_on_device("InputVerifier")
- .with_min_level(log::Level::Trace),
+ .with_max_level(log::LevelFilter::Trace),
);
Self {
name: name.to_owned(),
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 650c930..60feb53 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -32,37 +32,31 @@
namespace android {
+namespace {
+bool operator==(const InputChannel& left, const InputChannel& right) {
+ struct stat lhs, rhs;
+ if (fstat(left.getFd(), &lhs) != 0) {
+ return false;
+ }
+ if (fstat(right.getFd(), &rhs) != 0) {
+ return false;
+ }
+ // If file descriptors are pointing to same inode they are duplicated fds.
+ return left.getName() == right.getName() &&
+ left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino;
+}
+} // namespace
+
class InputChannelTest : public testing::Test {
};
+TEST_F(InputChannelTest, ClientAndServerTokensMatch) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
-TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) {
- // Our purpose here is to verify that the input channel destructor closes the
- // file descriptor provided to it. One easy way is to provide it with one end
- // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
- Pipe pipe;
-
- android::base::unique_fd sendFd(pipe.sendFd);
-
- std::unique_ptr<InputChannel> inputChannel =
- InputChannel::create("channel name", std::move(sendFd), new BBinder());
-
- EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created";
- EXPECT_STREQ("channel name", inputChannel->getName().c_str())
- << "channel should have provided name";
- EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd";
-
- // InputChannel should be the owner of the file descriptor now
- ASSERT_FALSE(sendFd.ok());
-}
-
-TEST_F(InputChannelTest, SetAndGetToken) {
- Pipe pipe;
- sp<IBinder> token = new BBinder();
- std::unique_ptr<InputChannel> channel =
- InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token);
-
- EXPECT_EQ(token, channel->getConnectionToken());
+ status_t result =
+ InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel);
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+ EXPECT_EQ(serverChannel->getConnectionToken(), clientChannel->getConnectionToken());
}
TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
@@ -71,8 +65,7 @@
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
- ASSERT_EQ(OK, result)
- << "should have successfully opened a channel pair";
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
// Name
EXPECT_STREQ("channel name (server)", serverChannel->getName().c_str())
@@ -235,25 +228,6 @@
}
}
-TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
-
- status_t result =
- InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel);
-
- ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
-
- InputChannel chan;
- Parcel parcel;
- ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel));
- parcel.setDataPosition(0);
- chan.readFromParcel(&parcel);
-
- EXPECT_EQ(chan == *serverChannel, true)
- << "inputchannel should be equal after parceling and unparceling.\n"
- << "name " << chan.getName() << " name " << serverChannel->getName();
-}
-
TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) {
std::unique_ptr<InputChannel> serverChannel, clientChannel;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index fc84bbf..330cc66 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -1016,7 +1016,7 @@
.fakeOutputDataspace = fakeDataspace}));
// Turn on dithering when dimming beyond this (arbitrary) threshold...
- static constexpr float kDimmingThreshold = 0.2f;
+ static constexpr float kDimmingThreshold = 0.9f;
// ...or we're rendering an HDR layer down to an 8-bit target
// Most HDR standards require at least 10-bits of color depth for source content, so we
// can just extract the transfer function rather than dig into precise gralloc layout.
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index d992aa5..cc92bc3 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -21,6 +21,18 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "libsensor_flags",
+ package: "com.android.hardware.libsensor.flags",
+ srcs: ["libsensor_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "libsensor_flags_c_lib",
+ host_supported: true,
+ aconfig_declarations: "libsensor_flags",
+}
+
cc_library {
name: "libsensor",
@@ -52,6 +64,10 @@
"android.companion.virtual.virtualdevice_aidl-cpp",
],
+ static_libs: [
+ "libsensor_flags_c_lib",
+ ],
+
export_include_dirs: ["include"],
export_shared_lib_headers: [
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index b82a79f..9411e20 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -37,6 +37,8 @@
#include <sensor/Sensor.h>
#include <sensor/SensorEventQueue.h>
+#include <com_android_hardware_libsensor_flags.h>
+
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -192,6 +194,9 @@
}
status_t SensorManager::assertStateLocked() {
+#if COM_ANDROID_HARDWARE_LIBSENSOR_FLAGS(SENSORMANAGER_PING_BINDER)
+ if (mSensorServer == nullptr) {
+#else
bool initSensorManager = false;
if (mSensorServer == nullptr) {
initSensorManager = true;
@@ -203,6 +208,7 @@
}
}
if (initSensorManager) {
+#endif
waitForSensorService(&mSensorServer);
LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL");
diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig
new file mode 100644
index 0000000..ef4d737
--- /dev/null
+++ b/libs/sensor/libsensor_flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.hardware.libsensor.flags"
+
+flag {
+ name: "sensormanager_ping_binder"
+ namespace: "sensors"
+ description: "Whether to pingBinder on SensorManager init"
+ bug: "322228259"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index a7955cf..2d6838b 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -110,6 +110,7 @@
],
static_libs: [
"libattestation",
+ "libperfetto_client_experimental",
"libpalmrejection",
"libui-types",
],
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 4863513..823df67 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -260,13 +260,16 @@
}
// Used by tests only.
-binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) {
+binder::Status InputManager::createInputChannel(const std::string& name,
+ android::os::InputChannelCore* outChannel) {
IPCThreadState* ipc = IPCThreadState::self();
- const int uid = ipc->getCallingUid();
+ const uid_t uid = ipc->getCallingUid();
if (uid != AID_SHELL && uid != AID_ROOT) {
- ALOGE("Invalid attempt to register input channel over IPC"
- "from non shell/root entity (PID: %d)", ipc->getCallingPid());
- return binder::Status::ok();
+ LOG(ERROR) << __func__ << " can only be called by SHELL or ROOT users, "
+ << "but was called from UID " << uid;
+ return binder::Status::
+ fromExceptionCode(EX_SECURITY,
+ "This uid is not allowed to call createInputChannel");
}
base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index df944ef..c479aaf 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -136,7 +136,8 @@
void dump(std::string& dump) override;
status_t dump(int fd, const Vector<String16>& args) override;
- binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+ binder::Status createInputChannel(const std::string& name,
+ android::os::InputChannelCore* outChannel) override;
binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
binder::Status setFocusedWindow(const gui::FocusRequest&) override;
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index c7bacee..582eeeb 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -49,6 +49,7 @@
"Monitor.cpp",
"TouchedWindow.cpp",
"TouchState.cpp",
+ "trace/*.cpp",
],
}
@@ -72,6 +73,7 @@
static_libs: [
"libattestation",
"libgui_window_info_static",
+ "libperfetto_client_experimental",
],
target: {
android: {
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 2153d8a..bc090cf 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -284,7 +284,8 @@
DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> targetFlags,
const ui::Transform& transform, const ui::Transform& rawTransform,
- float globalScaleFactor)
+ float globalScaleFactor, gui::Uid targetUid, int64_t vsyncId,
+ std::optional<int32_t> windowId)
: seq(nextSeq()),
eventEntry(std::move(eventEntry)),
targetFlags(targetFlags),
@@ -292,7 +293,10 @@
rawTransform(rawTransform),
globalScaleFactor(globalScaleFactor),
deliveryTime(0),
- resolvedFlags(0) {
+ resolvedFlags(0),
+ targetUid(targetUid),
+ vsyncId(vsyncId),
+ windowId(windowId) {
switch (this->eventEntry->type) {
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*this->eventEntry);
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index a915805..9e5d346 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -227,9 +227,19 @@
int32_t resolvedFlags;
+ // Information about the dispatch window used for tracing. We avoid holding a window handle
+ // here because information in a window handle may be dynamically updated within the lifespan
+ // of this dispatch entry.
+ gui::Uid targetUid;
+ int64_t vsyncId;
+ // The window that this event is targeting. The only case when this windowId is not populated
+ // is when dispatching an event to a global monitor.
+ std::optional<int32_t> windowId;
+
DispatchEntry(std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform,
- const ui::Transform& rawTransform, float globalScaleFactor);
+ const ui::Transform& rawTransform, float globalScaleFactor, gui::Uid targetUid,
+ int64_t vsyncId, std::optional<int32_t> windowId);
DispatchEntry(const DispatchEntry&) = delete;
DispatchEntry& operator=(const DispatchEntry&) = delete;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c349a58..d706c4d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -35,6 +35,7 @@
#include <input/PrintTools.h>
#include <input/TraceTools.h>
#include <openssl/mem.h>
+#include <private/android_filesystem_config.h>
#include <unistd.h>
#include <utils/Trace.h>
@@ -51,6 +52,8 @@
#include "Connection.h"
#include "DebugConfig.h"
#include "InputDispatcher.h"
+#include "trace/InputTracer.h"
+#include "trace/InputTracingPerfettoBackend.h"
#define INDENT " "
#define INDENT2 " "
@@ -75,6 +78,14 @@
namespace {
+// Input tracing is only available on debuggable builds (userdebug and eng) when the feature
+// flag is enabled. When the flag is changed, tracing will only be available after reboot.
+bool isInputTracingEnabled() {
+ static const std::string buildType = base::GetProperty("ro.build.type", "user");
+ static const bool isUserdebugOrEng = buildType == "userdebug" || buildType == "eng";
+ return input_flags::enable_input_event_tracing() && isUserdebugOrEng;
+}
+
template <class Entry>
void ensureEventTraced(const Entry& entry) {
if (!entry.traceTracker) {
@@ -359,14 +370,22 @@
return i;
}
-std::unique_ptr<DispatchEntry> createDispatchEntry(
- const InputTarget& inputTarget, std::shared_ptr<const EventEntry> eventEntry,
- ftl::Flags<InputTarget::Flags> inputTargetFlags) {
+std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+ std::shared_ptr<const EventEntry> eventEntry,
+ ftl::Flags<InputTarget::Flags> inputTargetFlags,
+ int64_t vsyncId) {
+ const sp<WindowInfoHandle> win = inputTarget.windowHandle;
+ const std::optional<int32_t> windowId =
+ win ? std::make_optional(win->getInfo()->id) : std::nullopt;
+ // Assume the only targets that are not associated with a window are global monitors, and use
+ // the system UID for global monitors for tracing purposes.
+ const gui::Uid uid = win ? win->getInfo()->ownerUid : gui::Uid(AID_SYSTEM);
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
inputTarget.displayTransform,
- inputTarget.globalScaleFactor);
+ inputTarget.globalScaleFactor, uid, vsyncId,
+ windowId);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -413,7 +432,7 @@
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
firstPointerTransform, inputTarget.displayTransform,
- inputTarget.globalScaleFactor);
+ inputTarget.globalScaleFactor, uid, vsyncId, windowId);
return dispatchEntry;
}
@@ -804,7 +823,9 @@
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
- : InputDispatcher(policy, nullptr) {}
+ : InputDispatcher(policy,
+ isInputTracingEnabled() ? std::make_unique<trace::impl::PerfettoBackend>()
+ : nullptr) {}
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
@@ -833,7 +854,7 @@
mKeyRepeatState.lastKeyEntry = nullptr;
if (traceBackend) {
- // TODO: Create input tracer instance.
+ mTracer = std::make_unique<trace::impl::InputTracer>(std::move(traceBackend));
}
mLastUserActivityTimes.fill(0);
@@ -1961,6 +1982,7 @@
isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS
: CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS);
CancelationOptions options(mode, "input event injection failed");
+ options.displayId = entry->displayId;
synthesizeCancelationEventsForMonitorsLocked(options);
return true;
}
@@ -3333,10 +3355,11 @@
void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
std::shared_ptr<const EventEntry> eventEntry,
const InputTarget& inputTarget) {
+ // TODO(b/210460522): Verify all targets excluding global monitors are associated with a window.
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
- createDispatchEntry(inputTarget, eventEntry, inputTarget.flags);
+ createDispatchEntry(inputTarget, eventEntry, inputTarget.flags, mWindowInfosVsyncId);
// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
// different EventEntry than what was passed in.
@@ -3455,7 +3478,7 @@
<< cancelEvent->getDescription();
std::unique_ptr<DispatchEntry> cancelDispatchEntry =
createDispatchEntry(inputTarget, std::move(cancelEvent),
- ftl::Flags<InputTarget::Flags>());
+ ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId);
// Send these cancel events to the queue before sending the event from the new
// device.
@@ -5827,7 +5850,7 @@
for (const auto& [token, connection] : mConnectionsByToken) {
dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
"status=%s, monitor=%s, responsive=%s\n",
- connection->inputChannel->getFd().get(),
+ connection->inputChannel->getFd(),
connection->getInputChannelName().c_str(),
connection->getWindowName().c_str(),
ftl::enum_string(connection->status).c_str(),
@@ -5914,7 +5937,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
const sp<IBinder>& token = serverChannel->getConnectionToken();
- auto&& fd = serverChannel->getFd();
+ const int fd = serverChannel->getFd();
std::shared_ptr<Connection> connection =
std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false,
mIdGenerator);
@@ -5927,7 +5950,7 @@
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
- mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
nullptr);
} // release lock
@@ -5957,7 +5980,7 @@
std::shared_ptr<Connection> connection =
std::make_shared<Connection>(serverChannel, /*monitor=*/true, mIdGenerator);
const sp<IBinder>& token = serverChannel->getConnectionToken();
- auto&& fd = serverChannel->getFd();
+ const int fd = serverChannel->getFd();
if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
ALOGE("Created a new connection, but the token %p is already known", token.get());
@@ -5968,7 +5991,7 @@
mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid);
- mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
nullptr);
}
@@ -6007,7 +6030,7 @@
removeMonitorChannelLocked(connectionToken);
}
- mLooper->removeFd(connection->inputChannel->getFd().get());
+ mLooper->removeFd(connection->inputChannel->getFd());
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
new file mode 100644
index 0000000..a61fa85
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 {
+
+void AndroidInputEventProtoConverter::toProtoMotionEvent(const TracedMotionEvent& event,
+ proto::AndroidMotionEvent& outProto) {
+ 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);
+ outProto.set_classification(static_cast<int32_t>(event.classification));
+ outProto.set_cursor_position_x(event.xCursorPosition);
+ outProto.set_cursor_position_y(event.yCursorPosition);
+ outProto.set_flags(event.flags);
+ outProto.set_policy_flags(event.policyFlags);
+
+ 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);
+ axisEntry->set_value(coords.values[axisIndex]);
+ }
+ }
+}
+
+void AndroidInputEventProtoConverter::toProtoKeyEvent(const TracedKeyEvent& event,
+ proto::AndroidKeyEvent& outProto) {
+ 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);
+ outProto.set_key_code(event.keyCode);
+ outProto.set_scan_code(event.scanCode);
+ outProto.set_meta_state(event.metaState);
+ outProto.set_repeat_count(event.repeatCount);
+ outProto.set_flags(event.flags);
+ outProto.set_policy_flags(event.policyFlags);
+}
+
+void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(
+ const InputTracingBackendInterface::WindowDispatchArgs& args,
+ proto::AndroidWindowInputDispatchEvent& outProto) {
+ 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 (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 rawXY =
+ MotionEvent::calculateTransformedXY(motion->source, args.rawTransform,
+ motion->pointerCoords[i].getXYValue());
+ pointerProto->set_x_in_display(rawXY.x);
+ pointerProto->set_y_in_display(rawXY.y);
+
+ const auto& coords = motion->pointerCoords[i];
+ const auto coordsInWindow =
+ MotionEvent::calculateTransformedCoords(motion->source, 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];
+ if (coords.values[axisIndex] != axisValueInWindow) {
+ auto* axisEntry = pointerProto->add_axis_value_in_window();
+ axisEntry->set_axis(axis);
+ axisEntry->set_value(axisValueInWindow);
+ }
+ }
+ }
+ }
+}
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
new file mode 100644
index 0000000..8a46f15
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
@@ -0,0 +1,39 @@
+/*
+ * 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 <perfetto/trace/android/android_input_event.pbzero.h>
+
+#include "InputTracingBackendInterface.h"
+
+namespace proto = perfetto::protos::pbzero;
+
+namespace android::inputdispatcher::trace {
+
+/**
+ * Write traced events into Perfetto protos.
+ */
+class AndroidInputEventProtoConverter {
+public:
+ static void toProtoMotionEvent(const TracedMotionEvent& event,
+ proto::AndroidMotionEvent& outProto);
+ static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto);
+ static void toProtoWindowDispatchEvent(const InputTracingBackendInterface::WindowDispatchArgs&,
+ proto::AndroidWindowInputDispatchEvent& outProto);
+};
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
new file mode 100644
index 0000000..b065729
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputTracer"
+
+#include "InputTracer.h"
+
+#include <android-base/logging.h>
+#include <utils/AndroidThreads.h>
+
+namespace android::inputdispatcher::trace::impl {
+
+namespace {
+
+TracedEvent createTracedEvent(const MotionEntry& e) {
+ return TracedMotionEvent{e.id,
+ e.eventTime,
+ e.policyFlags,
+ e.deviceId,
+ e.source,
+ e.displayId,
+ e.action,
+ e.actionButton,
+ e.flags,
+ e.metaState,
+ e.buttonState,
+ e.classification,
+ e.edgeFlags,
+ e.xPrecision,
+ e.yPrecision,
+ e.xCursorPosition,
+ e.yCursorPosition,
+ e.downTime,
+ e.pointerProperties,
+ e.pointerCoords};
+}
+
+TracedEvent createTracedEvent(const KeyEntry& e) {
+ return TracedKeyEvent{e.id, e.eventTime, e.policyFlags, e.deviceId, e.source,
+ e.displayId, e.action, e.keyCode, e.scanCode, e.metaState,
+ e.downTime, e.flags, e.repeatCount};
+}
+
+} // namespace
+
+// --- InputTracer ---
+
+InputTracer::InputTracer(std::unique_ptr<InputTracingBackendInterface> backend)
+ : mTracerThread(&InputTracer::threadLoop, this), mBackend(std::move(backend)) {}
+
+InputTracer::~InputTracer() {
+ {
+ std::scoped_lock lock(mLock);
+ mThreadExit = true;
+ }
+ mThreadWakeCondition.notify_all();
+ mTracerThread.join();
+}
+
+std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) {
+ std::scoped_lock lock(mLock);
+ TracedEvent traced;
+
+ if (entry.type == EventEntry::Type::MOTION) {
+ const auto& motion = static_cast<const MotionEntry&>(entry);
+ traced = createTracedEvent(motion);
+ } else if (entry.type == EventEntry::Type::KEY) {
+ const auto& key = static_cast<const KeyEntry&>(entry);
+ traced = createTracedEvent(key);
+ } else {
+ LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
+ }
+
+ return std::make_unique<EventTrackerImpl>(*this, std::move(traced));
+}
+
+void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
+ const InputTarget& target) {
+ std::scoped_lock lock(mLock);
+ auto& cookieState = getState(cookie);
+ if (!cookieState) {
+ LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()";
+ }
+ // TODO(b/210460522): Determine if the event is sensitive based on the target.
+}
+
+void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
+ {
+ std::scoped_lock lock(mLock);
+ auto& cookieState = getState(cookie);
+ if (!cookieState) {
+ LOG(FATAL) << "Traced event was already logged. "
+ "eventProcessingComplete() was likely called more than once.";
+ }
+ mTraceQueue.emplace_back(std::move(*cookieState));
+ cookieState.reset();
+ } // release lock
+
+ mThreadWakeCondition.notify_all();
+}
+
+void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
+ const EventTrackerInterface* cookie) {
+ {
+ std::scoped_lock lock(mLock);
+ const EventEntry& entry = *dispatchEntry.eventEntry;
+
+ TracedEvent traced;
+ if (entry.type == EventEntry::Type::MOTION) {
+ const auto& motion = static_cast<const MotionEntry&>(entry);
+ traced = createTracedEvent(motion);
+ } else if (entry.type == EventEntry::Type::KEY) {
+ const auto& key = static_cast<const KeyEntry&>(entry);
+ traced = createTracedEvent(key);
+ } else {
+ LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
+ }
+
+ if (!cookie) {
+ // This event was not tracked as an inbound event, so trace it now.
+ mTraceQueue.emplace_back(traced);
+ }
+
+ // The vsyncId only has meaning if the event is targeting a window.
+ const int32_t windowId = dispatchEntry.windowId.value_or(0);
+ const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;
+
+ mDispatchTraceQueue.emplace_back(std::move(traced), dispatchEntry.deliveryTime,
+ dispatchEntry.resolvedFlags, dispatchEntry.targetUid,
+ vsyncId, windowId, dispatchEntry.transform,
+ dispatchEntry.rawTransform);
+ } // release lock
+
+ mThreadWakeCondition.notify_all();
+}
+
+std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) {
+ return static_cast<const EventTrackerImpl&>(cookie).mLockedState;
+}
+
+void InputTracer::threadLoop() {
+ androidSetThreadName("InputTracer");
+
+ while (true) {
+ std::vector<const EventState> eventsToTrace;
+ std::vector<const WindowDispatchArgs> dispatchEventsToTrace;
+ {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ if (mThreadExit) {
+ return;
+ }
+ if (mTraceQueue.empty() && mDispatchTraceQueue.empty()) {
+ // Wait indefinitely until the thread is awoken.
+ mThreadWakeCondition.wait(lock);
+ }
+
+ mTraceQueue.swap(eventsToTrace);
+ mDispatchTraceQueue.swap(dispatchEventsToTrace);
+ } // release lock
+
+ // Trace the events into the backend without holding the lock to reduce the amount of
+ // work performed in the critical section.
+ writeEventsToBackend(eventsToTrace, dispatchEventsToTrace);
+ eventsToTrace.clear();
+ dispatchEventsToTrace.clear();
+ }
+}
+
+void InputTracer::writeEventsToBackend(
+ const std::vector<const EventState>& events,
+ const std::vector<const WindowDispatchArgs>& dispatchEvents) {
+ for (const auto& event : events) {
+ if (auto* motion = std::get_if<TracedMotionEvent>(&event.event); motion != nullptr) {
+ mBackend->traceMotionEvent(*motion);
+ } else {
+ mBackend->traceKeyEvent(std::get<TracedKeyEvent>(event.event));
+ }
+ }
+
+ for (const auto& dispatchArgs : dispatchEvents) {
+ mBackend->traceWindowDispatch(dispatchArgs);
+ }
+}
+
+// --- InputTracer::EventTrackerImpl ---
+
+InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event)
+ : mTracer(tracer), mLockedState(event) {}
+
+InputTracer::EventTrackerImpl::~EventTrackerImpl() {
+ {
+ std::scoped_lock lock(mTracer.mLock);
+ if (!mLockedState) {
+ // This event has already been written to the trace as expected.
+ return;
+ }
+ // We're still holding on to the state, which means it hasn't yet been written to the trace.
+ // Write it to the trace now.
+ // TODO(b/210460522): Determine why/where the event is being destroyed before
+ // eventProcessingComplete() is called.
+ mTracer.mTraceQueue.emplace_back(std::move(*mLockedState));
+ mLockedState.reset();
+ } // release lock
+
+ mTracer.mThreadWakeCondition.notify_all();
+}
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
new file mode 100644
index 0000000..9fe395d
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -0,0 +1,99 @@
+/*
+ * 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 "InputTracerInterface.h"
+
+#include <android-base/thread_annotations.h>
+#include <gui/WindowInfo.h>
+
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <unordered_set>
+#include <vector>
+
+#include "../Entry.h"
+#include "InputTracingBackendInterface.h"
+
+namespace android::inputdispatcher::trace::impl {
+
+/**
+ * The tracer implementation for InputDispatcher.
+ *
+ * InputTracer is thread-safe, so it can be called from any thread. Upon construction, InputTracer
+ * will start its own thread that it uses for write events into the tracing backend. That is the
+ * one and only thread that will interact with the tracing backend, since the Perfetto backend
+ * uses thread-local storage.
+ *
+ * See the documentation in InputTracerInterface for the API surface.
+ */
+class InputTracer : public InputTracerInterface {
+public:
+ explicit InputTracer(std::unique_ptr<InputTracingBackendInterface>);
+ ~InputTracer() override;
+ InputTracer(const InputTracer&) = delete;
+ InputTracer& operator=(const InputTracer&) = delete;
+
+ std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override;
+ void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override;
+ void eventProcessingComplete(const EventTrackerInterface&) override;
+ void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override;
+
+private:
+ std::mutex mLock;
+ std::thread mTracerThread;
+ bool mThreadExit GUARDED_BY(mLock){false};
+ std::condition_variable mThreadWakeCondition;
+ std::unique_ptr<InputTracingBackendInterface> mBackend;
+
+ // The state of a tracked event.
+ struct EventState {
+ const TracedEvent event;
+ // TODO(b/210460522): Add additional args for tracking event sensitivity and
+ // dispatch target UIDs.
+ };
+ std::vector<const EventState> mTraceQueue GUARDED_BY(mLock);
+ using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs;
+ std::vector<const WindowDispatchArgs> mDispatchTraceQueue GUARDED_BY(mLock);
+
+ // Provides thread-safe access to the state from an event tracker cookie.
+ std::optional<EventState>& getState(const EventTrackerInterface&) REQUIRES(mLock);
+
+ // Implementation of the event tracker cookie.
+ class EventTrackerImpl : public EventTrackerInterface {
+ public:
+ explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry);
+ virtual ~EventTrackerImpl() override;
+
+ private:
+ InputTracer& mTracer;
+ // This event tracker cookie will only hold the state as long as it has not been written
+ // to the trace. The state is released when the event is written to the trace.
+ mutable std::optional<EventState> mLockedState;
+
+ // Only allow InputTracer access to the locked state through getTrackerState() to ensure
+ // that the InputTracer lock is held when this is accessed.
+ friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&);
+ };
+
+ void threadLoop();
+ void writeEventsToBackend(const std::vector<const EventState>& events,
+ const std::vector<const WindowDispatchArgs>& dispatchEvents);
+};
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index b430a5b..bc47817 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -16,11 +16,65 @@
#pragma once
-#include "../Entry.h"
+#include <gui/PidUid.h>
+#include <input/Input.h>
+#include <ui/Transform.h>
+
+#include <array>
+#include <variant>
+#include <vector>
namespace android::inputdispatcher::trace {
/**
+ * A representation of an Android KeyEvent used by the tracing backend.
+ */
+struct TracedKeyEvent {
+ int32_t id;
+ nsecs_t eventTime;
+ uint32_t policyFlags;
+ int32_t deviceId;
+ uint32_t source;
+ int32_t displayId;
+ int32_t action;
+ int32_t keyCode;
+ int32_t scanCode;
+ int32_t metaState;
+ nsecs_t downTime;
+ int32_t flags;
+ int32_t repeatCount;
+};
+
+/**
+ * A representation of an Android MotionEvent used by the tracing backend.
+ */
+struct TracedMotionEvent {
+ int32_t id;
+ nsecs_t eventTime;
+ uint32_t policyFlags;
+ int32_t deviceId;
+ uint32_t source;
+ int32_t displayId;
+ int32_t action;
+ int32_t actionButton;
+ int32_t flags;
+ int32_t metaState;
+ int32_t buttonState;
+ MotionClassification classification;
+ int32_t edgeFlags;
+ float xPrecision;
+ float yPrecision;
+ float xCursorPosition;
+ float yCursorPosition;
+ nsecs_t downTime;
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+};
+
+/** A representation of a traced input event. */
+using TracedEvent = std::variant<TracedKeyEvent, TracedMotionEvent>;
+
+/**
* An interface for the tracing backend, used for setting a custom backend for testing.
*/
class InputTracingBackendInterface {
@@ -28,14 +82,14 @@
virtual ~InputTracingBackendInterface() = default;
/** Trace a KeyEvent. */
- virtual void traceKeyEvent(const KeyEntry&) const = 0;
+ virtual void traceKeyEvent(const TracedKeyEvent&) const = 0;
/** Trace a MotionEvent. */
- virtual void traceMotionEvent(const MotionEntry&) const = 0;
+ virtual void traceMotionEvent(const TracedMotionEvent&) const = 0;
/** Trace an event being sent to a window. */
struct WindowDispatchArgs {
- std::variant<MotionEntry, KeyEntry> eventEntry;
+ TracedEvent eventEntry;
nsecs_t deliveryTime;
int32_t resolvedFlags;
gui::Uid targetUid;
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
new file mode 100644
index 0000000..4442ad8
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputTracer"
+
+#include "InputTracingPerfettoBackend.h"
+
+#include "AndroidInputEventProtoConverter.h"
+
+#include <android-base/logging.h>
+#include <perfetto/trace/android/android_input_event.pbzero.h>
+
+namespace android::inputdispatcher::trace::impl {
+
+namespace {
+
+constexpr auto INPUT_EVENT_TRACE_DATA_SOURCE_NAME = "android.input.inputevent";
+
+} // namespace
+
+// --- PerfettoBackend::InputEventDataSource ---
+
+void PerfettoBackend::InputEventDataSource::OnStart(const perfetto::DataSourceBase::StartArgs&) {
+ LOG(INFO) << "Starting perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME;
+}
+
+void PerfettoBackend::InputEventDataSource::OnStop(const perfetto::DataSourceBase::StopArgs&) {
+ LOG(INFO) << "Stopping perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME;
+ InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { ctx.Flush(); });
+}
+
+// --- PerfettoBackend ---
+
+std::once_flag PerfettoBackend::sDataSourceRegistrationFlag{};
+
+PerfettoBackend::PerfettoBackend() {
+ // Use a once-flag to ensure that the data source is only registered once per boot, since
+ // we never unregister the InputEventDataSource.
+ std::call_once(sDataSourceRegistrationFlag, []() {
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ perfetto::Tracing::Initialize(args);
+
+ // Register our custom data source for input event tracing.
+ perfetto::DataSourceDescriptor dsd;
+ dsd.set_name(INPUT_EVENT_TRACE_DATA_SOURCE_NAME);
+ InputEventDataSource::Register(dsd);
+ LOG(INFO) << "InputTracer initialized for data source: "
+ << INPUT_EVENT_TRACE_DATA_SOURCE_NAME;
+ });
+}
+
+void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) const {
+ InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto tracePacket = ctx.NewTracePacket();
+ auto* inputEvent = tracePacket->set_android_input_event();
+ auto* dispatchMotion = inputEvent->set_dispatcher_motion_event();
+ AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion);
+ });
+}
+
+void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) const {
+ InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto tracePacket = ctx.NewTracePacket();
+ auto* inputEvent = tracePacket->set_android_input_event();
+ auto* dispatchKey = inputEvent->set_dispatcher_key_event();
+ AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey);
+ });
+}
+
+void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) const {
+ InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto tracePacket = ctx.NewTracePacket();
+ auto* inputEventProto = tracePacket->set_android_input_event();
+ auto* dispatchEventProto = inputEventProto->set_dispatcher_window_dispatch_event();
+ AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs,
+ *dispatchEventProto);
+ });
+}
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
new file mode 100644
index 0000000..2777cfe
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
@@ -0,0 +1,66 @@
+/*
+ * 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 "InputTracingBackendInterface.h"
+
+#include <perfetto/tracing.h>
+#include <mutex>
+
+namespace android::inputdispatcher::trace::impl {
+
+/**
+ * The tracing backend that writes events into ongoing Perfetto traces.
+ *
+ * Example shell command to take an input trace from Perfetto:
+ *
+ * adb shell perfetto \
+ * -c - --txt \
+ * -o /data/misc/perfetto-traces/trace.input-trace \
+ * <<END
+ * buffers: {
+ * size_kb: 5000
+ * fill_policy: RING_BUFFER
+ * }
+ * data_sources: {
+ * config {
+ * name: "android.input.inputevent"
+ * }
+ * }
+ * END
+ */
+class PerfettoBackend : public InputTracingBackendInterface {
+public:
+ PerfettoBackend();
+ ~PerfettoBackend() override = default;
+
+ void traceKeyEvent(const TracedKeyEvent&) const override;
+ void traceMotionEvent(const TracedMotionEvent&) const override;
+ void traceWindowDispatch(const WindowDispatchArgs&) const override;
+
+ class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> {
+ public:
+ void OnSetup(const SetupArgs&) override {}
+ void OnStart(const StartArgs&) override;
+ void OnStop(const StopArgs&) override;
+ };
+
+private:
+ static std::once_flag sDataSourceRegistrationFlag;
+};
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 9abfef1..79c8a4b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -226,6 +226,9 @@
// True to enable tap-to-click on touchpads.
bool touchpadTapToClickEnabled;
+ // True to enable tap dragging on touchpads.
+ bool touchpadTapDraggingEnabled;
+
// True to enable a zone on the right-hand side of touchpads where clicks will be turned into
// context (a.k.a. "right") clicks.
bool touchpadRightClickZoneEnabled;
@@ -266,6 +269,7 @@
touchpadPointerSpeed(0),
touchpadNaturalScrollingEnabled(true),
touchpadTapToClickEnabled(true),
+ touchpadTapDraggingEnabled(false),
touchpadRightClickZoneEnabled(false),
stylusButtonMotionEventsEnabled(true),
stylusPointerIconEnabled(false) {}
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index bdc1640..b990dd5 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -397,6 +397,8 @@
.setBoolValues({config.touchpadNaturalScrollingEnabled});
mPropertyProvider.getProperty("Tap Enable")
.setBoolValues({config.touchpadTapToClickEnabled});
+ mPropertyProvider.getProperty("Tap Drag Enable")
+ .setBoolValues({config.touchpadTapDraggingEnabled});
mPropertyProvider.getProperty("Button Right Click Zone Enable")
.setBoolValues({config.touchpadRightClickZoneEnabled});
}
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
index da72134..25dfb03 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -70,7 +70,9 @@
/// incremented using `AIBinder_incStrong`. See `binder::unstable_api::new_spibinder`.
unsafe fn create_inputflinger_rust(callback: *mut ffi::IInputFlingerRustBootstrapCallbackAIBinder) {
logger::init(
- logger::Config::default().with_tag_on_device(LOG_TAG).with_min_level(log::Level::Trace),
+ logger::Config::default()
+ .with_tag_on_device(LOG_TAG)
+ .with_max_level(log::LevelFilter::Trace),
);
let callback = callback as *mut AIBinder;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 55aa226..553cb70 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -44,6 +44,7 @@
"EventHub_test.cpp",
"FakeEventHub.cpp",
"FakeInputReaderPolicy.cpp",
+ "FakeInputTracingBackend.cpp",
"FakePointerController.cpp",
"FocusResolver_test.cpp",
"GestureConverter_test.cpp",
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp
new file mode 100644
index 0000000..77d35fb
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 "FakeInputTracingBackend.h"
+
+#include <android-base/logging.h>
+#include <utils/Errors.h>
+
+namespace android::inputdispatcher {
+
+namespace {
+
+constexpr auto TRACE_TIMEOUT = std::chrono::milliseconds(100);
+
+base::ResultError<> error(const std::ostringstream& ss) {
+ return base::ResultError(ss.str(), BAD_VALUE);
+}
+
+} // namespace
+
+// --- VerifyingTrace ---
+
+void VerifyingTrace::expectKeyDispatchTraced(const KeyEvent& event) {
+ std::scoped_lock lock(mLock);
+ mExpectedEvents.emplace_back(event);
+}
+
+void VerifyingTrace::expectMotionDispatchTraced(const MotionEvent& event) {
+ std::scoped_lock lock(mLock);
+ mExpectedEvents.emplace_back(event);
+}
+
+void VerifyingTrace::verifyExpectedEventsTraced() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ base::Result<void> result;
+ mEventTracedCondition.wait_for(lock, TRACE_TIMEOUT, [&]() REQUIRES(mLock) {
+ for (const auto& expectedEvent : mExpectedEvents) {
+ std::visit([&](const auto& event)
+ REQUIRES(mLock) { result = verifyEventTraced(event); },
+ expectedEvent);
+ if (!result.ok()) {
+ return false;
+ }
+ }
+ return true;
+ });
+
+ EXPECT_TRUE(result.ok())
+ << "Timed out waiting for all expected events to be traced successfully: "
+ << result.error().message();
+}
+
+void VerifyingTrace::reset() {
+ std::scoped_lock lock(mLock);
+ mTracedEvents.clear();
+ mExpectedEvents.clear();
+}
+
+template <typename Event>
+base::Result<void> VerifyingTrace::verifyEventTraced(const Event& expectedEvent) const {
+ std::ostringstream msg;
+
+ auto tracedEventsIt = mTracedEvents.find(expectedEvent.getId());
+ if (tracedEventsIt == mTracedEvents.end()) {
+ msg << "Expected event with ID 0x" << std::hex << expectedEvent.getId()
+ << " to be traced, but it was not.\n"
+ << "Expected event: " << expectedEvent;
+ return error(msg);
+ }
+
+ return {};
+}
+
+// --- FakeInputTracingBackend ---
+
+void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) const {
+ {
+ std::scoped_lock lock(mTrace->mLock);
+ mTrace->mTracedEvents.emplace(event.id);
+ }
+ mTrace->mEventTracedCondition.notify_all();
+}
+
+void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) const {
+ {
+ std::scoped_lock lock(mTrace->mLock);
+ mTrace->mTracedEvents.emplace(event.id);
+ }
+ mTrace->mEventTracedCondition.notify_all();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h
new file mode 100644
index 0000000..e5dd546
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputTracingBackend.h
@@ -0,0 +1,88 @@
+/*
+ * 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 "../dispatcher/trace/InputTracingBackendInterface.h"
+
+#include <android-base/result.h>
+#include <android-base/thread_annotations.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <unordered_set>
+#include <vector>
+
+namespace android::inputdispatcher {
+
+/**
+ * A class representing an input trace, used to make assertions on what was traced by
+ * InputDispatcher in tests. This class is thread-safe.
+ */
+class VerifyingTrace {
+public:
+ VerifyingTrace() = default;
+
+ /** Add an expectation for a key event to be traced. */
+ void expectKeyDispatchTraced(const KeyEvent& event);
+
+ /** Add an expectation for a motion event to be traced. */
+ void expectMotionDispatchTraced(const MotionEvent& event);
+
+ /**
+ * Wait and verify that all expected events are traced.
+ * This is a lenient verifier that does not expect the events to be traced in the order
+ * that the events were expected, and does not fail if there are events that are traced that
+ * were not expected. Verifying does not clear the expectations.
+ */
+ void verifyExpectedEventsTraced();
+
+ /** Reset the trace and clear all expectations. */
+ void reset();
+
+private:
+ std::mutex mLock;
+ std::condition_variable mEventTracedCondition;
+ std::unordered_set<uint32_t /*eventId*/> mTracedEvents GUARDED_BY(mLock);
+ std::vector<std::variant<KeyEvent, MotionEvent>> mExpectedEvents GUARDED_BY(mLock);
+
+ friend class FakeInputTracingBackend;
+
+ // Helper to verify that the given event appears as expected in the trace. If the verification
+ // fails, the error message describes why.
+ template <typename Event>
+ base::Result<void> verifyEventTraced(const Event&) const REQUIRES(mLock);
+};
+
+/**
+ * A backend implementation for input tracing that records events to the provided
+ * VerifyingTrace used for testing.
+ */
+class FakeInputTracingBackend : public trace::InputTracingBackendInterface {
+public:
+ FakeInputTracingBackend(std::shared_ptr<VerifyingTrace> trace) : mTrace(trace) {}
+
+private:
+ std::shared_ptr<VerifyingTrace> mTrace;
+
+ void traceKeyEvent(const trace::TracedKeyEvent& entry) const override;
+ void traceMotionEvent(const trace::TracedMotionEvent& entry) const override;
+ void traceWindowDispatch(const WindowDispatchArgs& entry) const override {}
+};
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f1f4a61..e826ddc 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -17,6 +17,7 @@
#include "../dispatcher/InputDispatcher.h"
#include "../BlockingQueue.h"
#include "FakeApplicationHandle.h"
+#include "FakeInputTracingBackend.h"
#include "TestEventMatchers.h"
#include <NotifyArgsBuilders.h>
@@ -658,14 +659,22 @@
// --- InputDispatcherTest ---
+// The trace is a global variable for now, to avoid having to pass it into all of the
+// FakeWindowHandles created throughout the tests.
+// TODO(b/210460522): Update the tests to avoid the need to have the trace be a global variable.
+static std::shared_ptr<VerifyingTrace> gVerifyingTrace = std::make_shared<VerifyingTrace>();
+
class InputDispatcherTest : public testing::Test {
protected:
std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy;
std::unique_ptr<InputDispatcher> mDispatcher;
void SetUp() override {
+ gVerifyingTrace->reset();
mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
- mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
+ mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy,
+ std::make_unique<FakeInputTracingBackend>(
+ gVerifyingTrace));
mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
// Start InputDispatcher thread
@@ -676,6 +685,7 @@
ASSERT_EQ(OK, mDispatcher->stop());
mFakePolicy.reset();
mDispatcher.reset();
+ ASSERT_NO_FATAL_FAILURE(gVerifyingTrace->verifyExpectedEventsTraced());
}
/**
@@ -1113,7 +1123,7 @@
sp<IBinder> getToken() { return mConsumer.getChannel()->getConnectionToken(); }
- int getChannelFd() { return mConsumer.getChannel()->getFd().get(); }
+ int getChannelFd() { return mConsumer.getChannel()->getFd(); }
private:
InputConsumer mConsumer;
@@ -1395,11 +1405,7 @@
}
std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent() {
- if (mInputReceiver == nullptr) {
- ADD_FAILURE() << "Invalid receive event on window with no receiver";
- return std::make_pair(std::nullopt, nullptr);
- }
- return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ return receive();
}
void finishEvent(uint32_t sequenceNum) {
@@ -1444,6 +1450,7 @@
static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
friend class sp<FakeWindowHandle>;
+ // FakeWindowHandle uses this consume method to ensure received events are added to the trace.
std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true) {
if (mInputReceiver == nullptr) {
LOG(FATAL) << "Cannot consume event from a window with no input event receiver";
@@ -1452,8 +1459,40 @@
if (event == nullptr) {
ADD_FAILURE() << "Consume failed: no event";
}
+ expectReceivedEventTraced(event);
return event;
}
+
+ // FakeWindowHandle uses this receive method to ensure received events are added to the trace.
+ std::pair<std::optional<uint32_t /*seq*/>, std::unique_ptr<InputEvent>> receive() {
+ if (mInputReceiver == nullptr) {
+ ADD_FAILURE() << "Invalid receive event on window with no receiver";
+ return std::make_pair(std::nullopt, nullptr);
+ }
+ auto out = mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ const auto& [_, event] = out;
+ expectReceivedEventTraced(event);
+ return std::move(out);
+ }
+
+ void expectReceivedEventTraced(const std::unique_ptr<InputEvent>& event) {
+ if (!event) {
+ return;
+ }
+
+ switch (event->getType()) {
+ case InputEventType::KEY: {
+ gVerifyingTrace->expectKeyDispatchTraced(static_cast<KeyEvent&>(*event));
+ break;
+ }
+ case InputEventType::MOTION: {
+ gVerifyingTrace->expectMotionDispatchTraced(static_cast<MotionEvent&>(*event));
+ break;
+ }
+ default:
+ break;
+ }
+ }
};
std::atomic<int32_t> FakeWindowHandle::sId{1};
@@ -6151,6 +6190,219 @@
monitor.assertNoEvents();
}
+/**
+ * Two displays
+ * The first monitor has a foreground window, a monitor
+ * The second window has only one monitor.
+ * We first inject a Down event into the first display, this injection should succeed and both
+ * the foreground window and monitor should receive a down event, then inject a Down event into
+ * the second display as well, this injection should fail, at this point, the first display
+ * window and monitor should not receive a cancel or any other event.
+ * Continue to inject Move and UP events to the first display, the events should be received
+ * normally by the foreground window and monitor.
+ */
+TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCanceledWhenAnotherEmptyDisplayReceiveEvents) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "The down event injected into the first display should succeed";
+
+ window->consumeMotionDown();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
+ {100, 200}))
+ << "The down event injected into the second display should fail since there's no "
+ "touchable window";
+
+ // Continue to inject event to first display.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 220}))
+ << "The move event injected into the first display should succeed";
+
+ window->consumeMotionMove();
+ monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {110, 220}))
+ << "The up event injected into the first display should succeed";
+
+ window->consumeMotionUp();
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ window->assertNoEvents();
+ monitor.assertNoEvents();
+ secondMonitor.assertNoEvents();
+}
+
+/**
+ * Two displays
+ * There is a monitor and foreground window on each display.
+ * First, we inject down events into each of the two displays, at this point, the foreground windows
+ * and monitors on both displays should receive down events.
+ * At this point, the foreground window of the second display goes away, the gone window should
+ * receive the cancel event, and the other windows and monitors should not receive any events.
+ * Inject a move event into the second display. At this point, the injection should fail because
+ * the second display no longer has a foreground window. At this point, the monitor on the second
+ * display should receive a cancel event, and any windows or monitors on the first display should
+ * not receive any events, and any subsequent injection of events into the second display should
+ * also fail.
+ * Continue to inject events into the first display, and the events should all be injected
+ * successfully and received normally.
+ */
+TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCancelWhenAnotherDisplayMonitorTouchCanceled) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> secondWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "SecondForeground",
+ SECOND_DISPLAY_ID);
+
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+ // There is a foreground window on both displays.
+ mDispatcher->onWindowInfosChanged({{*window->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "The down event injected into the first display should succeed";
+
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
+ {100, 200}))
+ << "The down event injected into the second display should succeed";
+
+ secondWindow->consumeMotionDown(SECOND_DISPLAY_ID);
+ secondMonitor.consumeMotionDown(SECOND_DISPLAY_ID);
+
+ // Now second window is gone away.
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // The gone window should receive a cancel, and the monitor on the second display should not
+ // receive any events.
+ secondWindow->consumeMotionCancel(SECOND_DISPLAY_ID);
+ secondMonitor.assertNoEvents();
+
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ SECOND_DISPLAY_ID, {110, 220}))
+ << "The move event injected into the second display should fail because there's no "
+ "touchable window";
+ // Now the monitor on the second display should receive a cancel event.
+ secondMonitor.consumeMotionCancel(SECOND_DISPLAY_ID);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "The move event injected into the first display should succeed";
+
+ window->consumeMotionMove();
+ monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
+ {110, 220}))
+ << "The up event injected into the second display should fail because there's no "
+ "touchable window";
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {110, 220}))
+ << "The up event injected into the first display should succeed";
+
+ window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ window->assertNoEvents();
+ monitor.assertNoEvents();
+ secondWindow->assertNoEvents();
+ secondMonitor.assertNoEvents();
+}
+
+/**
+ * One display with transform
+ * There is a foreground window and a monitor on the display
+ * Inject down event and move event sequentially, the foreground window and monitor can receive down
+ * event and move event, then let the foreground window go away, the foreground window receives
+ * cancel event, inject move event again, the monitor receives cancel event, all the events received
+ * by the monitor should be with the same transform as the display
+ */
+TEST_F(InputDispatcherMonitorTest, MonitorTouchCancelEventWithDisplayTransform) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+
+ ui::Transform transform;
+ transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
+
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = ADISPLAY_ID_DEFAULT;
+ displayInfo.transform = transform;
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {displayInfo}, 0, 0});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "The down event injected should succeed";
+
+ window->consumeMotionDown();
+ std::unique_ptr<MotionEvent> downMotionEvent = monitor.consumeMotion();
+ EXPECT_EQ(transform, downMotionEvent->getTransform());
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, downMotionEvent->getAction());
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 220}))
+ << "The move event injected should succeed";
+
+ window->consumeMotionMove();
+ std::unique_ptr<MotionEvent> moveMotionEvent = monitor.consumeMotion();
+ EXPECT_EQ(transform, moveMotionEvent->getTransform());
+ EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, moveMotionEvent->getAction());
+
+ // Let foreground window gone
+ mDispatcher->onWindowInfosChanged({{}, {displayInfo}, 0, 0});
+
+ // Foreground window should receive a cancel event, but not the monitor.
+ window->consumeMotionCancel();
+
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 220}))
+ << "The move event injected should failed";
+ // Now foreground should not receive any events, but monitor should receive a cancel event
+ // with transform that same as display's display.
+ std::unique_ptr<MotionEvent> cancelMotionEvent = monitor.consumeMotion();
+ EXPECT_EQ(transform, cancelMotionEvent->getTransform());
+ EXPECT_EQ(ADISPLAY_ID_DEFAULT, cancelMotionEvent->getDisplayId());
+ EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, cancelMotionEvent->getAction());
+
+ // Other event inject to this display should fail.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 220}))
+ << "The up event injected should fail because the touched window was removed";
+ window->assertNoEvents();
+ monitor.assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, TestMoveEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -7090,12 +7342,9 @@
sp<FakeWindowHandle> mWindow;
virtual void SetUp() override {
- mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
- mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
- mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
- mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
- ASSERT_EQ(OK, mDispatcher->start());
+ InputDispatcherTest::SetUp();
+ mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
setUpWindow();
}
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
index be765cc..c2bf275 100644
--- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -123,6 +123,7 @@
config.touchpadPointerSpeed = fdp.ConsumeIntegralInRange(-7, 7);
config.touchpadNaturalScrollingEnabled = fdp.ConsumeBool();
config.touchpadTapToClickEnabled = fdp.ConsumeBool();
+ config.touchpadTapDraggingEnabled = fdp.ConsumeBool();
config.touchpadRightClickZoneEnabled = fdp.ConsumeBool();
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 9e35717..575a30e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -97,13 +97,13 @@
MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
MOCK_CONST_METHOD2(getModes,
std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t));
- MOCK_CONST_METHOD1(getActiveMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getActiveMode, ftl::Expected<hal::HWConfigId, status_t>(PhysicalDisplayId));
MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
MOCK_CONST_METHOD0(isUsingVrComposer, bool());
MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId));
MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
- MOCK_CONST_METHOD2(getDisplayVsyncPeriod, status_t(PhysicalDisplayId, nsecs_t*));
+ MOCK_CONST_METHOD1(getDisplayVsyncPeriod, ftl::Expected<nsecs_t, status_t>(PhysicalDisplayId));
MOCK_METHOD4(setActiveModeWithConstraints,
status_t(PhysicalDisplayId, hal::HWConfigId,
const hal::VsyncPeriodChangeConstraints&,
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4f81482..5f20cd9 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -239,10 +239,8 @@
return 0;
}
- nsecs_t vsyncPeriod;
- const auto status = mHwComposer.getDisplayVsyncPeriod(physicalId, &vsyncPeriod);
- if (status == NO_ERROR) {
- return vsyncPeriod;
+ if (const auto vsyncPeriodOpt = mHwComposer.getDisplayVsyncPeriod(physicalId).value_opt()) {
+ return *vsyncPeriodOpt;
}
return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 17f6f31..362ab9c 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -343,7 +343,9 @@
}
mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback);
- AIBinder_setMinSchedulerPolicy(mAidlComposerCallback->asBinder().get(), SCHED_FIFO, 2);
+
+ ndk::SpAIBinder binder = mAidlComposerCallback->asBinder();
+ AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2);
const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback);
if (!status.isOk()) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index cf1c3c1..bac24c7 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -343,14 +343,18 @@
return modes;
}
-std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displayId) const {
- RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt);
+ftl::Expected<hal::HWConfigId, status_t> HWComposer::getActiveMode(
+ PhysicalDisplayId displayId) const {
+ RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX));
const auto hwcId = *fromPhysicalDisplayId(displayId);
hal::HWConfigId configId;
const auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId));
+ if (error == hal::Error::BAD_CONFIG) {
+ return ftl::Unexpected(NO_INIT);
+ }
- RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, std::nullopt);
+ RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, ftl::Unexpected(UNKNOWN_ERROR));
return configId;
}
@@ -376,20 +380,20 @@
return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
}
-status_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId,
- nsecs_t* outVsyncPeriod) const {
- RETURN_IF_INVALID_DISPLAY(displayId, 0);
+ftl::Expected<nsecs_t, status_t> HWComposer::getDisplayVsyncPeriod(
+ PhysicalDisplayId displayId) const {
+ RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX));
if (!isVsyncPeriodSwitchSupported(displayId)) {
- return INVALID_OPERATION;
+ return ftl::Unexpected(INVALID_OPERATION);
}
+
const auto hwcId = *fromPhysicalDisplayId(displayId);
Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
- auto error =
+ const auto error =
static_cast<hal::Error>(mComposer->getDisplayVsyncPeriod(hwcId, &vsyncPeriodNanos));
- RETURN_IF_HWC_ERROR(error, displayId, 0);
- *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
- return NO_ERROR;
+ RETURN_IF_HWC_ERROR(error, displayId, ftl::Unexpected(UNKNOWN_ERROR));
+ return static_cast<nsecs_t>(vsyncPeriodNanos);
}
std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index fb32ff4..7fbbb1a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -25,6 +25,7 @@
#include <vector>
#include <android-base/thread_annotations.h>
+#include <ftl/expected.h>
#include <ftl/future.h>
#include <ui/DisplayIdentification.h>
#include <ui/FenceTime.h>
@@ -237,7 +238,7 @@
virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId,
int32_t maxFrameIntervalNs) const = 0;
- virtual std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const = 0;
+ virtual ftl::Expected<hal::HWConfigId, status_t> getActiveMode(PhysicalDisplayId) const = 0;
virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0;
@@ -247,8 +248,7 @@
// Composer 2.4
virtual ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0;
virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0;
- virtual status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId,
- nsecs_t* outVsyncPeriod) const = 0;
+ virtual ftl::Expected<nsecs_t, status_t> getDisplayVsyncPeriod(PhysicalDisplayId) const = 0;
virtual status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId,
const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
@@ -424,7 +424,7 @@
std::vector<HWCDisplayMode> getModes(PhysicalDisplayId,
int32_t maxFrameIntervalNs) const override;
- std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const override;
+ ftl::Expected<hal::HWConfigId, status_t> getActiveMode(PhysicalDisplayId) const override;
std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override;
@@ -435,8 +435,7 @@
// Composer 2.4
ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
- status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId,
- nsecs_t* outVsyncPeriod) const override;
+ ftl::Expected<nsecs_t, status_t> getDisplayVsyncPeriod(PhysicalDisplayId) const override;
status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId,
const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline* outTimeline) override;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index c8b1059..219a8e2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -78,11 +78,13 @@
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
+#include "Utils/FenceUtils.h"
#define DEBUG_RESIZE 0
#define EARLY_RELEASE_ENABLED false
namespace android {
+using namespace std::chrono_literals;
namespace {
constexpr int kDumpTableRowLength = 159;
@@ -2911,7 +2913,8 @@
}
void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack layerStack) {
+ ui::LayerStack layerStack,
+ std::function<FenceResult(FenceResult)>&& continuation) {
// If we are displayed on multiple displays in a single composition cycle then we would
// need to do careful tracking to enable the use of the mLastClientCompositionFence.
// For example we can only use it if all the displays are client comp, and we need
@@ -2946,11 +2949,41 @@
break;
}
}
+
+ if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) {
+ futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share();
+ }
+
if (ch != nullptr) {
ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
ch->name = mName;
+ } else if (FlagManager::getInstance().screenshot_fence_preservation()) {
+ // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots
+ // asynchronously, then make sure we don't drop the fence.
+ mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult),
+ std::move(continuation));
+ std::vector<FenceAndContinuation> mergedFences;
+ sp<Fence> prevFence = nullptr;
+ // For a layer that's frequently screenshotted, try to merge fences to make sure we don't
+ // grow unbounded.
+ for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
+ auto result = futureAndContinution.future.wait_for(0s);
+ if (result != std::future_status::ready) {
+ mergedFences.emplace_back(futureAndContinution);
+ continue;
+ }
+
+ mergeFence(getDebugName(), futureAndContinution.chain().get().value_or(Fence::NO_FENCE),
+ prevFence);
+ }
+ if (prevFence != nullptr) {
+ mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share());
+ }
+
+ mAdditionalPreviousReleaseFences.swap(mergedFences);
}
+
if (mBufferInfo.mBuffer) {
mPreviouslyPresentedLayerStacks.push_back(layerStack);
}
@@ -3440,6 +3473,15 @@
handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
handle->frameNumber = mDrawingState.frameNumber;
handle->previousFrameNumber = mDrawingState.previousFrameNumber;
+ if (FlagManager::getInstance().screenshot_fence_preservation() &&
+ mPreviousReleaseBufferEndpoint == handle->listener) {
+ // Add fences from previous screenshots now so that they can be dispatched to the
+ // client.
+ for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
+ handle->previousReleaseFences.emplace_back(futureAndContinution.chain());
+ }
+ mAdditionalPreviousReleaseFences.clear();
+ }
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c772e0e..dfd57c6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -555,7 +555,8 @@
const compositionengine::LayerFECompositionState* getCompositionState() const;
bool fenceHasSignaled() const;
void onPreComposition(nsecs_t refreshStartTime);
- void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack);
+ void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
+ std::function<FenceResult(FenceResult)>&& continuation = nullptr);
void setWasClientComposed(const sp<Fence>& fence) {
mLastClientCompositionFence = fence;
@@ -932,6 +933,19 @@
// the release fences from the correct displays when we release the last buffer
// from the layer.
std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
+ struct FenceAndContinuation {
+ ftl::SharedFuture<FenceResult> future;
+ std::function<FenceResult(FenceResult)> continuation;
+
+ ftl::SharedFuture<FenceResult> chain() const {
+ if (continuation) {
+ return ftl::Future(future).then(continuation).share();
+ } else {
+ return future;
+ }
+ }
+ };
+ std::vector<FenceAndContinuation> mAdditionalPreviousReleaseFences;
// Exposed so SurfaceFlinger can assert that it's held
const sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index c3709e5..7614453 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -843,9 +843,13 @@
const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
+ // A method for UI Toolkit to send the touch signal via "HighHint" category vote,
+ // which will touch boost when there are no ExplicitDefault layer votes. This is an
+ // incomplete solution but accounts for cases such as games that use `setFrameRate` with default
+ // compatibility to limit the frame rate, which should not have touch boost.
const bool hasInteraction = signals.touch || interactiveLayers > 0;
- if (hasInteraction && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 &&
- touchBoostForExplicitExact &&
+
+ if (hasInteraction && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost");
ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 47e7474..d86de84 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3293,7 +3293,7 @@
std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes(
PhysicalDisplayId displayId) const {
std::vector<HWComposer::HWCDisplayMode> hwcModes;
- std::optional<hal::HWDisplayId> activeModeHwcId;
+ std::optional<hal::HWConfigId> activeModeHwcIdOpt;
int attempt = 0;
constexpr int kMaxAttempts = 3;
@@ -3301,10 +3301,10 @@
hwcModes = getHwComposer().getModes(displayId,
scheduler::RefreshRateSelector::kMinSupportedFrameRate
.getPeriodNsecs());
- activeModeHwcId = getHwComposer().getActiveMode(displayId);
+ activeModeHwcIdOpt = getHwComposer().getActiveMode(displayId).value_opt();
- const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
- return mode.hwcId == activeModeHwcId;
+ const auto isActiveMode = [activeModeHwcIdOpt](const HWComposer::HWCDisplayMode& mode) {
+ return mode.hwcId == activeModeHwcIdOpt;
};
if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) {
@@ -3314,7 +3314,7 @@
if (attempt == kMaxAttempts) {
const std::string activeMode =
- activeModeHwcId ? std::to_string(*activeModeHwcId) : "unknown"s;
+ activeModeHwcIdOpt ? std::to_string(*activeModeHwcIdOpt) : "unknown"s;
ALOGE("HWC failed to report an active mode that is supported: activeModeHwcId=%s, "
"hwcModes={%s}",
activeMode.c_str(), base::Join(hwcModes, ", ").c_str());
@@ -3358,8 +3358,8 @@
// Keep IDs if modes have not changed.
const auto& modes = sameModes ? oldModes : newModes;
const DisplayModePtr activeMode =
- std::find_if(modes.begin(), modes.end(), [activeModeHwcId](const auto& pair) {
- return pair.second->getHwcId() == activeModeHwcId;
+ std::find_if(modes.begin(), modes.end(), [activeModeHwcIdOpt](const auto& pair) {
+ return pair.second->getHwcId() == activeModeHwcIdOpt;
})->second;
return {modes, activeMode};
@@ -5733,7 +5733,7 @@
case ISurfaceComposerClient::eFXSurfaceContainer:
case ISurfaceComposerClient::eFXSurfaceBufferState:
args.flags |= ISurfaceComposerClient::eNoColorFill;
- FMT_FALLTHROUGH;
+ [[fallthrough]];
case ISurfaceComposerClient::eFXSurfaceEffect: {
result = createBufferStateLayer(args, &outResult.handle, &layer);
std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
@@ -8159,14 +8159,23 @@
: ftl::yield(present()).share();
for (auto& [layer, layerFE] : layers) {
- layer->onLayerDisplayed(ftl::Future(presentFuture)
- .then([layerFE = std::move(layerFE)](FenceResult) {
- return layerFE->stealCompositionResult()
- .releaseFences.back()
- .first.get();
- })
- .share(),
- ui::INVALID_LAYER_STACK);
+ layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK,
+ [layerFE = std::move(layerFE)](FenceResult) {
+ if (FlagManager::getInstance()
+ .screenshot_fence_preservation()) {
+ const auto compositionResult =
+ layerFE->stealCompositionResult();
+ const auto& fences = compositionResult.releaseFences;
+ // CompositionEngine may choose to cull layers that
+ // aren't visible, so pass a non-fence.
+ return fences.empty() ? Fence::NO_FENCE
+ : fences.back().first.get();
+ } else {
+ return layerFE->stealCompositionResult()
+ .releaseFences.back()
+ .first.get();
+ }
+ });
}
return presentFuture;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 6a155c1..7b5298c 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -25,6 +25,7 @@
#include "TransactionCallbackInvoker.h"
#include "BackgroundExecutor.h"
+#include "Utils/FenceUtils.h"
#include <cinttypes>
@@ -127,33 +128,8 @@
sp<IBinder> surfaceControl = handle->surfaceControl.promote();
if (surfaceControl) {
sp<Fence> prevFence = nullptr;
-
for (const auto& future : handle->previousReleaseFences) {
- sp<Fence> currentFence = future.get().value_or(Fence::NO_FENCE);
- if (prevFence == nullptr && currentFence->getStatus() != Fence::Status::Invalid) {
- prevFence = std::move(currentFence);
- } else if (prevFence != nullptr) {
- // If both fences are signaled or both are unsignaled, we need to merge
- // them to get an accurate timestamp.
- if (prevFence->getStatus() != Fence::Status::Invalid &&
- prevFence->getStatus() == currentFence->getStatus()) {
- char fenceName[32] = {};
- snprintf(fenceName, 32, "%.28s", handle->name.c_str());
- sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, currentFence);
- if (mergedFence->isValid()) {
- prevFence = std::move(mergedFence);
- }
- } else if (currentFence->getStatus() == Fence::Status::Unsignaled) {
- // If one fence has signaled and the other hasn't, the unsignaled
- // fence will approximately correspond with the correct timestamp.
- // There's a small race if both fences signal at about the same time
- // and their statuses are retrieved with unfortunate timing. However,
- // by this point, they will have both signaled and only the timestamp
- // will be slightly off; any dependencies after this point will
- // already have been met.
- prevFence = std::move(currentFence);
- }
- }
+ mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
}
handle->previousReleaseFence = prevFence;
handle->previousReleaseFences.clear();
diff --git a/services/surfaceflinger/Utils/FenceUtils.h b/services/surfaceflinger/Utils/FenceUtils.h
new file mode 100644
index 0000000..f6f7006
--- /dev/null
+++ b/services/surfaceflinger/Utils/FenceUtils.h
@@ -0,0 +1,51 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include <ui/Fence.h>
+
+namespace android {
+
+// TODO: measure if Fence::merge is cheaper
+inline void mergeFence(const char* debugName, sp<Fence>&& incomingFence, sp<Fence>& prevFence) {
+ if (prevFence == nullptr && incomingFence->getStatus() != Fence::Status::Invalid) {
+ prevFence = std::move(incomingFence);
+ } else if (prevFence != nullptr) {
+ // If both fences are signaled or both are unsignaled, we need to merge
+ // them to get an accurate timestamp.
+ if (prevFence->getStatus() != Fence::Status::Invalid &&
+ prevFence->getStatus() == incomingFence->getStatus()) {
+ char fenceName[32] = {};
+ snprintf(fenceName, 32, "%.28s", debugName);
+ sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, incomingFence);
+ if (mergedFence->isValid()) {
+ prevFence = std::move(mergedFence);
+ }
+ } else if (incomingFence->getStatus() == Fence::Status::Unsignaled) {
+ // If one fence has signaled and the other hasn't, the unsignaled
+ // fence will approximately correspond with the correct timestamp.
+ // There's a small race if both fences signal at about the same time
+ // and their statuses are retrieved with unfortunate timing. However,
+ // by this point, they will have both signaled and only the timestamp
+ // will be slightly off; any dependencies after this point will
+ // already have been met.
+ prevFence = std::move(incomingFence);
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 255b517..b07e7ac 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -128,6 +128,7 @@
DUMP_READ_ONLY_FLAG(fp16_client_target);
DUMP_READ_ONLY_FLAG(game_default_frame_rate);
DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
+ DUMP_READ_ONLY_FLAG(screenshot_fence_preservation);
DUMP_READ_ONLY_FLAG(vulkan_renderengine);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
@@ -203,6 +204,7 @@
FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "")
+FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation")
FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
/// Trunk stable server flags ///
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 15938c0..2a30a40 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -68,6 +68,7 @@
bool fp16_client_target() const;
bool game_default_frame_rate() const;
bool enable_layer_command_batching() const;
+ bool screenshot_fence_preservation() const;
bool vulkan_renderengine() const;
protected:
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index 68237c8..682079f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -143,7 +143,6 @@
void invokeComposerHal2_2(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer);
void invokeComposerHal2_3(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer);
void invokeComposerHal2_4(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer);
- void getDisplayVsyncPeriod();
void setActiveModeWithConstraints();
void getDisplayIdentificationData();
void dumpHwc();
@@ -202,11 +201,6 @@
return display;
}
-void DisplayHardwareFuzzer::getDisplayVsyncPeriod() {
- nsecs_t outVsyncPeriod;
- mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId, &outVsyncPeriod);
-}
-
void DisplayHardwareFuzzer::setActiveModeWithConstraints() {
hal::VsyncPeriodChangeTimeline outTimeline;
mHwc.setActiveModeWithConstraints(mPhysicalDisplayId, kActiveConfig, {} /*constraints*/,
@@ -617,8 +611,7 @@
mHwc.getDisplayConnectionType(mPhysicalDisplayId);
mHwc.isVsyncPeriodSwitchSupported(mPhysicalDisplayId);
-
- getDisplayVsyncPeriod();
+ mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId);
setActiveModeWithConstraints();
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index f097a13..fcbef01 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -166,3 +166,11 @@
bug: "293371537"
is_fixed_read_only: true
}
+
+flag {
+ name: "screenshot_fence_preservation"
+ namespace: "core_graphics"
+ description: "Bug fix around screenshot fences"
+ bug: "302703346"
+ is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index a5c0657..a25804c 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -104,7 +104,7 @@
TEST_F(HWComposerTest, getActiveMode) {
// Unknown display.
- EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), std::nullopt);
+ EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), ftl::Unexpected(BAD_INDEX));
constexpr hal::HWDisplayId kHwcDisplayId = 2;
expectHotplugConnect(kHwcDisplayId);
@@ -117,14 +117,20 @@
EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _))
.WillOnce(Return(HalError::BAD_DISPLAY));
- EXPECT_EQ(mHwc.getActiveMode(info->id), std::nullopt);
+ EXPECT_EQ(mHwc.getActiveMode(info->id), ftl::Unexpected(UNKNOWN_ERROR));
+ }
+ {
+ EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _))
+ .WillOnce(Return(HalError::BAD_CONFIG));
+
+ EXPECT_EQ(mHwc.getActiveMode(info->id), ftl::Unexpected(NO_INIT));
}
{
constexpr hal::HWConfigId kConfigId = 42;
EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _))
.WillOnce(DoAll(SetArgPointee<1>(kConfigId), Return(HalError::NONE)));
- EXPECT_EQ(mHwc.getActiveMode(info->id), kConfigId);
+ EXPECT_EQ(mHwc.getActiveMode(info->id).value_opt(), kConfigId);
}
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 1e526ba..c03cbd7 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -1624,6 +1624,7 @@
EXPECT_EQ(120_Hz, actualFrameRateMode.fps);
EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId());
+ // No touch boost, for example a game that uses setFrameRate(30, default compatibility).
lr1.vote = LayerVoteType::ExplicitCategory;
lr1.frameRateCategory = FrameRateCategory::HighHint;
lr1.name = "ExplicitCategory HighHint";
@@ -1652,8 +1653,9 @@
lr2.frameRateCategory = FrameRateCategory::Low;
lr2.name = "ExplicitCategory Low";
actualFrameRateMode = selector.getBestFrameRateMode(layers);
- EXPECT_EQ(30_Hz, actualFrameRateMode.fps);
- EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId());
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualFrameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId());
lr1.vote = LayerVoteType::ExplicitCategory;
lr1.frameRateCategory = FrameRateCategory::HighHint;