Merge "Shift notifyConfigurationChanged policy call to InputReader" into main
diff --git a/include/input/Input.h b/include/input/Input.h
index 456977b..77d7448 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -196,6 +196,13 @@
#define MAX_POINTER_ID 31
/*
+ * Number of high resolution scroll units for one detent (scroll wheel click), as defined in
+ * evdev. This is relevant when an input device is emitting REL_WHEEL_HI_RES or REL_HWHEEL_HI_RES
+ * events.
+ */
+constexpr int32_t kEvdevHighResScrollUnitsPerDetent = 120;
+
+/*
* Declare a concrete type for the NDK's input event forward declaration.
*/
struct AInputEvent {
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index 9e48b08..c7b1970 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -24,7 +24,7 @@
/**
* An interface to receive batched input events. Even if you don't want batching, you still have to
* use this interface, and some of the events will be batched if your implementation is slow to
- * handle the incoming input.
+ * handle the incoming input. The events received by these callbacks are never null.
*/
class InputConsumerCallbacks {
public:
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
index 9bbaa0c..dabe45c 100644
--- a/include/input/VirtualInputDevice.h
+++ b/include/input/VirtualInputDevice.h
@@ -77,6 +77,8 @@
private:
static const std::map<int, int> BUTTON_CODE_MAPPING;
+ int32_t mAccumulatedHighResScrollX;
+ int32_t mAccumulatedHighResScrollY;
};
class VirtualTouchscreen : public VirtualInputDevice {
@@ -127,6 +129,9 @@
VirtualRotaryEncoder(android::base::unique_fd fd);
virtual ~VirtualRotaryEncoder() override;
bool writeScrollEvent(float scrollAmount, std::chrono::nanoseconds eventTime);
+
+private:
+ int32_t mAccumulatedHighResScrollAmount;
};
} // namespace android
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 6185b7b..c57c9cd 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -441,14 +441,6 @@
return INVALID_OPERATION;
}
-status_t BBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>&) {
- return INVALID_OPERATION;
-}
-
-status_t BBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>&) {
- return INVALID_OPERATION;
-}
-
status_t BBinder::dump(int /*fd*/, const Vector<String16>& /*args*/)
{
return NO_ERROR;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 59c5be7..6594aa6 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -557,14 +557,6 @@
}
}
-status_t BpBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>&) {
- return INVALID_OPERATION;
-}
-
-status_t BpBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>&) {
- return INVALID_OPERATION;
-}
-
void BpBinder::reportOneDeath(const Obituary& obit)
{
sp<DeathRecipient> recipient = obit.recipient.promote();
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 3f70e8c..e8fe555 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1585,10 +1585,15 @@
fdVariant = borrowed_fd(fd);
}
if (!mAllowFds) {
+ ALOGE("FDs are not allowed in this parcel. Both the service and the client must set "
+ "the FileDescriptorTransportMode and agree on the support.");
return FDS_NOT_ALLOWED;
}
switch (rpcFields->mSession->getFileDescriptorTransportMode()) {
case RpcSession::FileDescriptorTransportMode::NONE: {
+ ALOGE("FDs are not allowed in this RpcSession. Both the service and the client "
+ "must set "
+ "the FileDescriptorTransportMode and agree on the support.");
return FDS_NOT_ALLOWED;
}
case RpcSession::FileDescriptorTransportMode::UNIX:
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 802a3bc..135be89 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -50,13 +50,6 @@
void* cookie = nullptr, uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr);
- // Placeholders to test if adding virtual functions here breaks things.
- // Will be replaced by an actual API once things are verified to work.
- LIBBINDER_EXPORTED virtual status_t addFrozenStateChangeCallback(
- const wp<FrozenStateChangeCallback>& callback);
- LIBBINDER_EXPORTED virtual status_t removeFrozenStateChangeCallback(
- const wp<FrozenStateChangeCallback>& callback);
-
LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object,
void* cleanupCookie,
object_cleanup_func func) final;
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 0f52f6d..d7f74c4 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -66,12 +66,6 @@
void* cookie = nullptr, uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr);
- [[nodiscard]] virtual status_t addFrozenStateChangeCallback(
- const wp<FrozenStateChangeCallback>& recipient);
-
- [[nodiscard]] virtual status_t removeFrozenStateChangeCallback(
- const wp<FrozenStateChangeCallback>& recipient);
-
LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object,
void* cleanupCookie,
object_cleanup_func func) final;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 62d7354..4eb1c08 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -102,6 +102,10 @@
*/
virtual const String16& getInterfaceDescriptor() const = 0;
+ /**
+ * Last known alive status, from last call. May be arbitrarily stale.
+ * May be incorrect if a service returns an incorrect status code.
+ */
virtual bool isBinderAlive() const = 0;
virtual status_t pingBinder() = 0;
virtual status_t dump(int fd, const Vector<String16>& args) = 0;
@@ -198,14 +202,9 @@
virtual void binderDied(const wp<IBinder>& who) = 0;
};
- class FrozenStateChangeCallback : public virtual RefBase {
- public:
- virtual void onStateChanged(const wp<IBinder>& who, bool isFrozen) = 0;
- };
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
+ #if defined(__clang__)
+ #pragma clang diagnostic pop
+ #endif
/**
* Register the @a recipient for a notification if this binder
@@ -254,12 +253,6 @@
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr) = 0;
- // Placeholders. See Binder.h for details.
- [[nodiscard]] virtual status_t addFrozenStateChangeCallback(
- const wp<FrozenStateChangeCallback>& callback) = 0;
- [[nodiscard]] virtual status_t removeFrozenStateChangeCallback(
- const wp<FrozenStateChangeCallback>& callback) = 0;
-
virtual bool checkSubclass(const void* subclassID) const;
typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index cf7dc1a..bfba79f 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -264,7 +264,7 @@
}
}
- bool getBoolean(const std::string& key, bool* _Nonnull val) {
+ bool getBoolean(const std::string& key, bool* _Nonnull val) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_getBoolean(mPBundle, key.c_str(), val);
} else {
@@ -272,7 +272,7 @@
}
}
- bool getInt(const std::string& key, int32_t* _Nonnull val) {
+ bool getInt(const std::string& key, int32_t* _Nonnull val) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_getInt(mPBundle, key.c_str(), val);
} else {
@@ -280,7 +280,7 @@
}
}
- bool getLong(const std::string& key, int64_t* _Nonnull val) {
+ bool getLong(const std::string& key, int64_t* _Nonnull val) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_getLong(mPBundle, key.c_str(), val);
} else {
@@ -288,7 +288,7 @@
}
}
- bool getDouble(const std::string& key, double* _Nonnull val) {
+ bool getDouble(const std::string& key, double* _Nonnull val) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_getDouble(mPBundle, key.c_str(), val);
} else {
@@ -300,7 +300,7 @@
return (char*)malloc(bufferSizeBytes);
}
- bool getString(const std::string& key, std::string* _Nonnull val) {
+ bool getString(const std::string& key, std::string* _Nonnull val) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
char* outString = nullptr;
bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString,
@@ -318,7 +318,7 @@
bool getVecInternal(int32_t (*_Nonnull getVec)(const APersistableBundle* _Nonnull,
const char* _Nonnull, T* _Nullable, int32_t),
const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
- std::vector<T>* _Nonnull vec) {
+ std::vector<T>* _Nonnull vec) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
int32_t bytes = 0;
// call first with nullptr to get required size in bytes
@@ -340,28 +340,28 @@
return false;
}
- bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) {
+ bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
vec);
}
return false;
}
- bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) {
+ bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
vec);
}
return false;
}
- bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) {
+ bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
vec);
}
return false;
}
- bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) {
+ bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle,
key.c_str(), vec);
@@ -372,7 +372,7 @@
// Takes ownership of and frees the char** and its elements.
// Creates a new set or vector based on the array of char*.
template <typename T>
- T moveStringsInternal(char* _Nullable* _Nonnull strings, int32_t bufferSizeBytes) {
+ T moveStringsInternal(char* _Nullable* _Nonnull strings, int32_t bufferSizeBytes) const {
if (strings && bufferSizeBytes > 0) {
int32_t num = bufferSizeBytes / sizeof(char*);
T ret;
@@ -386,7 +386,7 @@
return T();
}
- bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) {
+ bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
&stringAllocator, nullptr);
@@ -403,7 +403,7 @@
return false;
}
- bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) {
+ bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle* bundle = nullptr;
bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle);
@@ -422,7 +422,7 @@
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
void* _Nullable),
- const APersistableBundle* _Nonnull pBundle) {
+ const APersistableBundle* _Nonnull pBundle) const {
// call first with nullptr to get required size in bytes
int32_t bytes = getTypedKeys(pBundle, nullptr, 0, &stringAllocator, nullptr);
if (bytes > 0) {
@@ -435,84 +435,84 @@
return {};
}
- std::set<std::string> getBooleanKeys() {
+ std::set<std::string> getBooleanKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getBooleanKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getIntKeys() {
+ std::set<std::string> getIntKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getIntKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getLongKeys() {
+ std::set<std::string> getLongKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getLongKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getDoubleKeys() {
+ std::set<std::string> getDoubleKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getDoubleKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getStringKeys() {
+ std::set<std::string> getStringKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getStringKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getBooleanVectorKeys() {
+ std::set<std::string> getBooleanVectorKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getIntVectorKeys() {
+ std::set<std::string> getIntVectorKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getLongVectorKeys() {
+ std::set<std::string> getLongVectorKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getDoubleVectorKeys() {
+ std::set<std::string> getDoubleVectorKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getStringVectorKeys() {
+ std::set<std::string> getStringVectorKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getPersistableBundleKeys() {
+ std::set<std::string> getPersistableBundleKeys() const {
if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle);
} else {
return {};
}
}
- std::set<std::string> getMonKeys() {
+ std::set<std::string> getMonKeys() const {
// :P
return {"c(o,o)b", "c(o,o)b"};
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 9b1b64a..3efd84f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1168,7 +1168,8 @@
socklen_t len = sizeof(serverAddr);
ret = getsockname(serverFd.get(), reinterpret_cast<sockaddr*>(&serverAddr), &len);
LOG_ALWAYS_FATAL_IF(0 != ret, "Failed to getsockname: %s", strerror(errno));
- LOG_ALWAYS_FATAL_IF(len < sizeof(serverAddr), "getsockname didn't read the full addr struct");
+ LOG_ALWAYS_FATAL_IF(len < static_cast<socklen_t>(sizeof(serverAddr)),
+ "getsockname didn't read the full addr struct");
ret = TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/));
LOG_ALWAYS_FATAL_IF(0 != ret, "Could not listen socket on port %u: %s", serverAddr.svm_port,
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index c2a7ebb..45ebc66 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -258,6 +258,7 @@
],
shared_libs: [
+ "android.companion.virtualdevice.flags-aconfig-cc-host",
"libbase",
"libbinder",
"libbinder_ndk",
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index b73ee65..0579967 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -18,6 +18,7 @@
#include <android/input.h>
#include <android/keycodes.h>
+#include <android_companion_virtualdevice_flags.h>
#include <fcntl.h>
#include <input/Input.h>
#include <input/VirtualInputDevice.h>
@@ -40,6 +41,8 @@
namespace android {
+namespace vd_flags = android::companion::virtualdevice::flags;
+
VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {}
VirtualInputDevice::~VirtualInputDevice() {
@@ -253,7 +256,10 @@
// clang-format on
};
-VirtualMouse::VirtualMouse(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
+VirtualMouse::VirtualMouse(unique_fd fd)
+ : VirtualInputDevice(std::move(fd)),
+ mAccumulatedHighResScrollX(0),
+ mAccumulatedHighResScrollY(0) {}
VirtualMouse::~VirtualMouse() {}
@@ -272,9 +278,47 @@
bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement,
std::chrono::nanoseconds eventTime) {
- return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement, eventTime) &&
- writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement, eventTime) &&
- writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
+ if (!vd_flags::high_resolution_scroll()) {
+ return writeInputEvent(EV_REL, REL_HWHEEL, static_cast<int32_t>(xAxisMovement),
+ eventTime) &&
+ writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(yAxisMovement),
+ eventTime) &&
+ writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
+ }
+
+ const auto highResScrollX =
+ static_cast<int32_t>(xAxisMovement * kEvdevHighResScrollUnitsPerDetent);
+ const auto highResScrollY =
+ static_cast<int32_t>(yAxisMovement * kEvdevHighResScrollUnitsPerDetent);
+ bool highResScrollResult =
+ writeInputEvent(EV_REL, REL_HWHEEL_HI_RES, highResScrollX, eventTime) &&
+ writeInputEvent(EV_REL, REL_WHEEL_HI_RES, highResScrollY, eventTime);
+ if (!highResScrollResult) {
+ return false;
+ }
+
+ // According to evdev spec, a high-resolution mouse needs to emit REL_WHEEL / REL_HWHEEL events
+ // in addition to high-res scroll events. Regular scroll events can approximate high-res scroll
+ // events, so we send a regular scroll event when the accumulated scroll motion reaches a detent
+ // (single mouse wheel click).
+ mAccumulatedHighResScrollX += highResScrollX;
+ mAccumulatedHighResScrollY += highResScrollY;
+ const int32_t scrollX = mAccumulatedHighResScrollX / kEvdevHighResScrollUnitsPerDetent;
+ const int32_t scrollY = mAccumulatedHighResScrollY / kEvdevHighResScrollUnitsPerDetent;
+ if (scrollX != 0) {
+ if (!writeInputEvent(EV_REL, REL_HWHEEL, scrollX, eventTime)) {
+ return false;
+ }
+ mAccumulatedHighResScrollX %= kEvdevHighResScrollUnitsPerDetent;
+ }
+ if (scrollY != 0) {
+ if (!writeInputEvent(EV_REL, REL_WHEEL, scrollY, eventTime)) {
+ return false;
+ }
+ mAccumulatedHighResScrollY %= kEvdevHighResScrollUnitsPerDetent;
+ }
+
+ return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
}
// --- VirtualTouchscreen ---
@@ -510,14 +554,38 @@
}
// --- VirtualRotaryEncoder ---
-VirtualRotaryEncoder::VirtualRotaryEncoder(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
+VirtualRotaryEncoder::VirtualRotaryEncoder(unique_fd fd)
+ : VirtualInputDevice(std::move(fd)), mAccumulatedHighResScrollAmount(0) {}
VirtualRotaryEncoder::~VirtualRotaryEncoder() {}
bool VirtualRotaryEncoder::writeScrollEvent(float scrollAmount,
std::chrono::nanoseconds eventTime) {
- return writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(scrollAmount), eventTime) &&
- writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
+ if (!vd_flags::high_resolution_scroll()) {
+ return writeInputEvent(EV_REL, REL_WHEEL, static_cast<int32_t>(scrollAmount), eventTime) &&
+ writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
+ }
+
+ const auto highResScrollAmount =
+ static_cast<int32_t>(scrollAmount * kEvdevHighResScrollUnitsPerDetent);
+ if (!writeInputEvent(EV_REL, REL_WHEEL_HI_RES, highResScrollAmount, eventTime)) {
+ return false;
+ }
+
+ // According to evdev spec, a high-resolution scroll device needs to emit REL_WHEEL / REL_HWHEEL
+ // events in addition to high-res scroll events. Regular scroll events can approximate high-res
+ // scroll events, so we send a regular scroll event when the accumulated scroll motion reaches a
+ // detent (single wheel click).
+ mAccumulatedHighResScrollAmount += highResScrollAmount;
+ const int32_t scroll = mAccumulatedHighResScrollAmount / kEvdevHighResScrollUnitsPerDetent;
+ if (scroll != 0) {
+ if (!writeInputEvent(EV_REL, REL_WHEEL, scroll, eventTime)) {
+ return false;
+ }
+ mAccumulatedHighResScrollAmount %= kEvdevHighResScrollUnitsPerDetent;
+ }
+
+ return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
}
} // namespace android
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index a2192cb..cbe021b 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -15,13 +15,6 @@
bug: "271455682"
}
-flag {
- name: "enable_gestures_library_timer_provider"
- 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"
@@ -87,13 +80,6 @@
}
flag {
- name: "remove_pointer_event_tracking_in_wm"
- namespace: "input"
- description: "Remove pointer event tracking in WM after the Pointer Icon Refactor"
- bug: "315321016"
-}
-
-flag {
name: "enable_new_mouse_pointer_ballistics"
namespace: "input"
description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones"
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index f49469c..e710613 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -52,7 +52,7 @@
const int32_t action;
const nsecs_t downTime;
const uint32_t seq;
- const int32_t eventId;
+ int32_t eventId;
const int32_t deviceId = 1;
const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
const ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT;
@@ -291,6 +291,7 @@
void publishAndConsumeKeyEvent();
void publishAndConsumeMotionStream();
void publishAndConsumeMotionDown(nsecs_t downTime);
+ void publishAndConsumeSinglePointerMultipleSamples(const size_t nSamples);
void publishAndConsumeBatchedMotionMove(nsecs_t downTime);
void publishAndConsumeFocusEvent();
void publishAndConsumeCaptureEvent();
@@ -298,6 +299,7 @@
void publishAndConsumeTouchModeEvent();
void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime,
const std::vector<Pointer>& pointers);
+
void TearDown() override {
// Destroy the consumer, flushing any of the pending ack's.
sendMessage(LooperMessage::DESTROY_CONSUMER);
@@ -519,6 +521,123 @@
{Pointer{.id = 0, .x = 20, .y = 30}});
}
+/*
+ * Decompose a potential multi-sampled MotionEvent into multiple MotionEvents
+ * with a single sample.
+ */
+std::vector<MotionEvent> splitBatchedMotionEvent(const MotionEvent& batchedMotionEvent) {
+ std::vector<MotionEvent> singleMotionEvents;
+ const size_t batchSize = batchedMotionEvent.getHistorySize() + 1;
+ for (size_t i = 0; i < batchSize; ++i) {
+ MotionEvent singleMotionEvent;
+ singleMotionEvent
+ .initialize(batchedMotionEvent.getId(), batchedMotionEvent.getDeviceId(),
+ batchedMotionEvent.getSource(), batchedMotionEvent.getDisplayId(),
+ batchedMotionEvent.getHmac(), batchedMotionEvent.getAction(),
+ batchedMotionEvent.getActionButton(), batchedMotionEvent.getFlags(),
+ batchedMotionEvent.getEdgeFlags(), batchedMotionEvent.getMetaState(),
+ batchedMotionEvent.getButtonState(),
+ batchedMotionEvent.getClassification(),
+ batchedMotionEvent.getTransform(), batchedMotionEvent.getXPrecision(),
+ batchedMotionEvent.getYPrecision(),
+ batchedMotionEvent.getRawXCursorPosition(),
+ batchedMotionEvent.getRawYCursorPosition(),
+ batchedMotionEvent.getRawTransform(), batchedMotionEvent.getDownTime(),
+ batchedMotionEvent.getHistoricalEventTime(/*historicalIndex=*/i),
+ batchedMotionEvent.getPointerCount(),
+ batchedMotionEvent.getPointerProperties(),
+ (batchedMotionEvent.getSamplePointerCoords() + i));
+ singleMotionEvents.push_back(singleMotionEvent);
+ }
+ return singleMotionEvents;
+}
+
+/*
+ * Simulates a single pointer touching the screen and leaving it there for a period of time.
+ * Publishes a DOWN event and consumes it right away. Then, publishes a sequence of MOVE
+ * samples for the same pointer, and waits until it has been consumed. Splits batched MotionEvents
+ * into individual samples. Checks the consumed MotionEvents against the published ones.
+ * This test is non-deterministic because it depends on the timing of arrival of events to the
+ * socket.
+ *
+ * @param nSamples The number of MOVE samples to publish before attempting consumption.
+ */
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMultipleSamples(
+ const size_t nSamples) {
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ const Pointer pointer(0, 20, 30);
+
+ const PublishMotionArgs argsDown(AMOTION_EVENT_ACTION_DOWN, downTime, {pointer}, mSeq);
+ const nsecs_t publishTimeOfDown = systemTime(SYSTEM_TIME_MONOTONIC);
+ publishMotionEvent(*mPublisher, argsDown);
+
+ // Consume the DOWN event.
+ ASSERT_TRUE(mMotionEvents.popWithTimeout(TIMEOUT).has_value());
+
+ verifyFinishedSignal(*mPublisher, mSeq, publishTimeOfDown);
+
+ std::vector<nsecs_t> publishTimes;
+ std::vector<PublishMotionArgs> argsMoves;
+ std::queue<uint32_t> publishedSequenceNumbers;
+
+ // Block Looper to increase the chance of batching events
+ {
+ std::scoped_lock l(mLock);
+ mLooperMayProceed = false;
+ }
+ sendMessage(LooperMessage::BLOCK_LOOPER);
+ {
+ std::unique_lock l(mLock);
+ mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; });
+ }
+
+ uint32_t firstSampleId;
+ for (size_t i = 0; i < nSamples; ++i) {
+ publishedSequenceNumbers.push(++mSeq);
+ PublishMotionArgs argsMove(AMOTION_EVENT_ACTION_MOVE, downTime, {pointer}, mSeq);
+ // A batched MotionEvent only has a single event id, currently determined when the
+ // MotionEvent is initialized. Therefore, to pass the eventId comparisons inside
+ // verifyArgsEqualToEvent, we need to override the event id of the published args to match
+ // the event id of the first sample inside the MotionEvent.
+ if (i == 0) {
+ firstSampleId = argsMove.eventId;
+ }
+ argsMove.eventId = firstSampleId;
+ publishTimes.push_back(systemTime(SYSTEM_TIME_MONOTONIC));
+ argsMoves.push_back(argsMove);
+ publishMotionEvent(*mPublisher, argsMove);
+ }
+
+ std::vector<MotionEvent> singleSampledMotionEvents;
+
+ // Unblock Looper
+ {
+ std::scoped_lock l(mLock);
+ mLooperMayProceed = true;
+ }
+ mNotifyLooperMayProceed.notify_all();
+
+ // We have no control over the socket behavior, so the consumer can receive
+ // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a
+ // mix of those)
+ while (singleSampledMotionEvents.size() != nSamples) {
+ const std::optional<std::unique_ptr<MotionEvent>> batchedMotionEvent =
+ mMotionEvents.popWithTimeout(TIMEOUT);
+ // The events received by these calls are never null
+ std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(**batchedMotionEvent);
+ singleSampledMotionEvents.insert(singleSampledMotionEvents.end(), splitMotionEvents.begin(),
+ splitMotionEvents.end());
+ }
+
+ // Consumer can choose to finish events in any order. For simplicity,
+ // we verify the events in sequence (since that is how the test is implemented).
+ for (size_t i = 0; i < nSamples; ++i) {
+ verifyArgsEqualToEvent(argsMoves[i], singleSampledMotionEvents[i]);
+ verifyFinishedSignal(*mPublisher, publishedSequenceNumbers.front(), publishTimes[i]);
+ publishedSequenceNumbers.pop();
+ }
+}
+
void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMove(
nsecs_t downTime) {
uint32_t seq = mSeq++;
@@ -814,4 +933,8 @@
ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
}
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishAndConsumeSinglePointer) {
+ publishAndConsumeSinglePointerMultipleSamples(3);
+}
+
} // namespace android
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 5261287..dd78049 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -300,7 +300,9 @@
if (result == 0) {
outPlanes->planeCount = 3;
outPlanes->planes[0].data = yuvData.y;
- if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010) {
+ // P010 is word-aligned 10-bit semiplaner, and YCbCr_422_I is a single interleaved plane
+ if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010 ||
+ format == AHARDWAREBUFFER_FORMAT_YCbCr_422_I) {
outPlanes->planes[0].pixelStride = 2;
} else {
outPlanes->planes[0].pixelStride = 1;
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
index 3a4c869..b5c56c5 100644
--- a/libs/tracing_perfetto/Android.bp
+++ b/libs/tracing_perfetto/Android.bp
@@ -40,6 +40,7 @@
],
shared_libs: [
+ "libbase",
"libcutils",
"libperfetto_c",
"android.os.flags-aconfig-cc-host",
diff --git a/libs/tracing_perfetto/include/trace_result.h b/libs/tracing_perfetto/include/trace_result.h
deleted file mode 100644
index f7581fc..0000000
--- a/libs/tracing_perfetto/include/trace_result.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TRACE_RESULT_H
-#define TRACE_RESULT_H
-
-namespace tracing_perfetto {
-
-enum class Result {
- SUCCESS,
- NOT_SUPPORTED,
- INVALID_INPUT,
-};
-
-}
-
-#endif // TRACE_RESULT_H
diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h
index 2c1c2a4..0b2b0af 100644
--- a/libs/tracing_perfetto/include/tracing_perfetto.h
+++ b/libs/tracing_perfetto/include/tracing_perfetto.h
@@ -19,35 +19,32 @@
#include <stdint.h>
-#include "trace_result.h"
-
namespace tracing_perfetto {
void registerWithPerfetto(bool test = false);
-Result traceBegin(uint64_t category, const char* name);
+void traceBegin(uint64_t category, const char* name);
-Result traceEnd(uint64_t category);
+void traceEnd(uint64_t category);
-Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie);
+void traceAsyncBegin(uint64_t category, const char* name, int32_t cookie);
-Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie);
+void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie);
-Result traceAsyncBeginForTrack(uint64_t category, const char* name,
+void traceAsyncBeginForTrack(uint64_t category, const char* name,
const char* trackName, int32_t cookie);
-Result traceAsyncEndForTrack(uint64_t category, const char* trackName,
+void traceAsyncEndForTrack(uint64_t category, const char* trackName,
int32_t cookie);
-Result traceInstant(uint64_t category, const char* name);
+void traceInstant(uint64_t category, const char* name);
-Result traceInstantForTrack(uint64_t category, const char* trackName,
+void traceInstantForTrack(uint64_t category, const char* trackName,
const char* name);
-Result traceCounter(uint64_t category, const char* name, int64_t value);
+void traceCounter(uint64_t category, const char* name, int64_t value);
bool isTagEnabled(uint64_t category);
-
} // namespace tracing_perfetto
#endif // TRACING_PERFETTO_H
diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp
index a35b0e0..d203467 100644
--- a/libs/tracing_perfetto/tests/Android.bp
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -26,6 +26,7 @@
static_libs: [
"libflagtest",
"libgmock",
+ "perfetto_trace_protos",
],
cflags: [
"-Wall",
@@ -35,6 +36,8 @@
"android.os.flags-aconfig-cc-host",
"libbase",
"libperfetto_c",
+ "liblog",
+ "libprotobuf-cpp-lite",
"libtracing_perfetto",
],
srcs: [
diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
index 7716b9a..e9fee2e 100644
--- a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
+++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
@@ -16,10 +16,10 @@
#include "tracing_perfetto.h"
-#include <thread>
-
#include <android_os.h>
#include <flag_macros.h>
+#include <thread>
+#include <unistd.h>
#include "gtest/gtest.h"
#include "perfetto/public/abi/data_source_abi.h"
@@ -45,67 +45,182 @@
#include "trace_categories.h"
#include "utils.h"
+#include "protos/perfetto/trace/trace.pb.h"
+#include "protos/perfetto/trace/trace_packet.pb.h"
+#include "protos/perfetto/trace/interned_data/interned_data.pb.h"
+
+#include <fstream>
+#include <iterator>
namespace tracing_perfetto {
-using ::perfetto::shlib::test_utils::AllFieldsWithId;
-using ::perfetto::shlib::test_utils::FieldView;
-using ::perfetto::shlib::test_utils::IdFieldView;
-using ::perfetto::shlib::test_utils::MsgField;
-using ::perfetto::shlib::test_utils::PbField;
-using ::perfetto::shlib::test_utils::StringField;
+using ::perfetto::protos::Trace;
+using ::perfetto::protos::TracePacket;
+using ::perfetto::protos::EventCategory;
+using ::perfetto::protos::EventName;
+using ::perfetto::protos::FtraceEvent;
+using ::perfetto::protos::FtraceEventBundle;
+using ::perfetto::protos::InternedData;
+
using ::perfetto::shlib::test_utils::TracingSession;
-using ::perfetto::shlib::test_utils::VarIntField;
-using ::testing::_;
-using ::testing::ElementsAre;
-using ::testing::UnorderedElementsAre;
const auto PERFETTO_SDK_TRACING = ACONFIG_FLAG(android::os, perfetto_sdk_tracing);
+// TODO(b/303199244): Add tests for all the library functions.
class TracingPerfettoTest : public testing::Test {
protected:
void SetUp() override {
- tracing_perfetto::registerWithPerfetto(true /* test */);
+ tracing_perfetto::registerWithPerfetto(false /* test */);
}
};
-// TODO(b/303199244): Add tests for all the library functions.
-
-TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstant,
- REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
- tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, "");
-
+Trace stopSession(TracingSession& tracing_session) {
+ tracing_session.FlushBlocking(5000);
tracing_session.StopBlocking();
std::vector<uint8_t> data = tracing_session.ReadBlocking();
+ std::string data_string(data.begin(), data.end());
+
+ perfetto::protos::Trace trace;
+ trace.ParseFromString(data_string);
+
+ return trace;
+}
+
+void verifyTrackEvent(const Trace& trace, const std::string expected_category,
+ const std::string& expected_name) {
bool found = false;
- for (struct PerfettoPbDecoderField trace_field : FieldView(data)) {
- ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number,
- MsgField(_)));
- IdFieldView track_event(
- trace_field, perfetto_protos_TracePacket_track_event_field_number);
- if (track_event.size() == 0) {
- continue;
+ for (const TracePacket& packet: trace.packet()) {
+ if (packet.has_track_event() && packet.has_interned_data()) {
+
+ const InternedData& interned_data = packet.interned_data();
+ if (interned_data.event_categories_size() > 0) {
+ const EventCategory& event_category = packet.interned_data().event_categories(0);
+ if (event_category.name() == expected_category) {
+ found = true;
+ }
+ }
+
+ if (interned_data.event_names_size() > 0) {
+ const EventName& event_name = packet.interned_data().event_names(0);
+ if (event_name.name() == expected_name) {
+ found &= true;
+ }
+ }
+
+ if (found) {
+ break;
+ }
}
- found = true;
- IdFieldView cat_iid_fields(
- track_event.front(),
- perfetto_protos_TrackEvent_category_iids_field_number);
- ASSERT_THAT(cat_iid_fields, ElementsAre(VarIntField(_)));
- uint64_t cat_iid = cat_iid_fields.front().value.integer64;
- EXPECT_THAT(
- trace_field,
- AllFieldsWithId(
- perfetto_protos_TracePacket_interned_data_field_number,
- ElementsAre(AllFieldsWithId(
- perfetto_protos_InternedData_event_categories_field_number,
- ElementsAre(MsgField(UnorderedElementsAre(
- PbField(perfetto_protos_EventCategory_iid_field_number,
- VarIntField(cat_iid)),
- PbField(perfetto_protos_EventCategory_name_field_number,
- StringField("input")))))))));
}
EXPECT_TRUE(found);
}
-} // namespace tracing_perfetto
\ No newline at end of file
+void verifyAtraceEvent(const Trace& trace, const std::string& expected_name) {
+ std::string expected_print_buf = "I|" + std::to_string(gettid()) + "|" + expected_name + "\n";
+
+ bool found = false;
+ for (const TracePacket& packet: trace.packet()) {
+ if (packet.has_ftrace_events()) {
+ const FtraceEventBundle& ftrace_events_bundle = packet.ftrace_events();
+
+ if (ftrace_events_bundle.event_size() > 0) {
+ const FtraceEvent& ftrace_event = ftrace_events_bundle.event(0);
+ if (ftrace_event.has_print() && (ftrace_event.print().buf() == expected_print_buf)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ EXPECT_TRUE(found);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfetto,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithPerfetto";
+
+ TracingSession tracing_session =
+ TracingSession::Builder().add_enabled_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace trace = stopSession(tracing_session);
+
+ verifyTrackEvent(trace, event_category, event_name);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithAtrace,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithAtrace";
+
+ TracingSession tracing_session =
+ TracingSession::Builder().add_atrace_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace trace = stopSession(tracing_session);
+
+ verifyAtraceEvent(trace, event_name);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtrace,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithPerfettoAndAtrace";
+
+ TracingSession tracing_session =
+ TracingSession::Builder()
+ .add_atrace_category(event_category)
+ .add_enabled_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace trace = stopSession(tracing_session);
+
+ verifyAtraceEvent(trace, event_name);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtraceAndPreferTrackEvent,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithPerfettoAndAtraceAndPreferTrackEvent";
+
+ TracingSession tracing_session =
+ TracingSession::Builder()
+ .add_atrace_category(event_category)
+ .add_atrace_category_prefer_sdk(event_category)
+ .add_enabled_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace trace = stopSession(tracing_session);
+
+ verifyTrackEvent(trace, event_category, event_name);
+}
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstantWithPerfettoAndAtraceConcurrently,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ std::string event_category = "input";
+ std::string event_name = "traceInstantWithPerfettoAndAtraceConcurrently";
+
+ TracingSession perfetto_tracing_session =
+ TracingSession::Builder()
+ .add_atrace_category(event_category)
+ .add_atrace_category_prefer_sdk(event_category)
+ .add_enabled_category(event_category).Build();
+
+ TracingSession atrace_tracing_session =
+ TracingSession::Builder()
+ .add_atrace_category(event_category)
+ .add_enabled_category(event_category).Build();
+
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, event_name.c_str());
+
+ Trace atrace_trace = stopSession(atrace_tracing_session);
+ Trace perfetto_trace = stopSession(perfetto_tracing_session);
+
+ verifyAtraceEvent(atrace_trace, event_name);
+ verifyAtraceEvent(perfetto_trace, event_name);
+}
+} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp
index 9c42028..8c4d4a8 100644
--- a/libs/tracing_perfetto/tests/utils.cpp
+++ b/libs/tracing_perfetto/tests/utils.cpp
@@ -26,6 +26,11 @@
#include "perfetto/public/protos/config/track_event/track_event_config.pzc.h"
#include "perfetto/public/tracing_session.h"
+#include "protos/perfetto/config/ftrace/ftrace_config.pb.h"
+#include "protos/perfetto/config/track_event/track_event_config.pb.h"
+#include "protos/perfetto/config/data_source_config.pb.h"
+#include "protos/perfetto/config/trace_config.pb.h"
+
namespace perfetto {
namespace shlib {
namespace test_utils {
@@ -44,63 +49,54 @@
} // namespace
TracingSession TracingSession::Builder::Build() {
- struct PerfettoPbMsgWriter writer;
- struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer);
+ perfetto::protos::TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(1024);
- struct perfetto_protos_TraceConfig cfg;
- PerfettoPbMsgInit(&cfg.msg, &writer);
+ auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config();
+ auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config();
+
+ track_event_ds_config->set_name("track_event");
+ track_event_ds_config->set_target_buffer(0);
+
+ ftrace_ds_config->set_name("linux.ftrace");
+ ftrace_ds_config->set_target_buffer(0);
{
- struct perfetto_protos_TraceConfig_BufferConfig buffers;
- perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers);
-
- perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024);
-
- perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers);
- }
-
- {
- struct perfetto_protos_TraceConfig_DataSource data_sources;
- perfetto_protos_TraceConfig_begin_data_sources(&cfg, &data_sources);
-
- {
- struct perfetto_protos_DataSourceConfig ds_cfg;
- perfetto_protos_TraceConfig_DataSource_begin_config(&data_sources,
- &ds_cfg);
-
- perfetto_protos_DataSourceConfig_set_cstr_name(&ds_cfg,
- data_source_name_.c_str());
- if (!enabled_categories_.empty() && !disabled_categories_.empty()) {
- perfetto_protos_TrackEventConfig te_cfg;
- perfetto_protos_DataSourceConfig_begin_track_event_config(&ds_cfg,
- &te_cfg);
- for (const std::string& cat : enabled_categories_) {
- perfetto_protos_TrackEventConfig_set_enabled_categories(
- &te_cfg, cat.data(), cat.size());
- }
- for (const std::string& cat : disabled_categories_) {
- perfetto_protos_TrackEventConfig_set_disabled_categories(
- &te_cfg, cat.data(), cat.size());
- }
- perfetto_protos_DataSourceConfig_end_track_event_config(&ds_cfg,
- &te_cfg);
+ auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config();
+ if (!atrace_categories_.empty()) {
+ ftrace_config->add_ftrace_events("ftrace/print");
+ for (const std::string& cat : atrace_categories_) {
+ ftrace_config->add_atrace_categories(cat);
}
- perfetto_protos_TraceConfig_DataSource_end_config(&data_sources, &ds_cfg);
+ for (const std::string& cat : atrace_categories_prefer_sdk_) {
+ ftrace_config->add_atrace_categories_prefer_sdk(cat);
+ }
}
-
- perfetto_protos_TraceConfig_end_data_sources(&cfg, &data_sources);
}
- size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer);
- std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]);
- PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size);
- PerfettoHeapBufferDestroy(hb, &writer.writer);
+
+ {
+ auto* track_event_config = track_event_ds_config->mutable_track_event_config();
+ if (!enabled_categories_.empty() || !disabled_categories_.empty()) {
+ for (const std::string& cat : enabled_categories_) {
+ track_event_config->add_enabled_categories(cat);
+ }
+
+ for (const std::string& cat : disabled_categories_) {
+ track_event_config->add_disabled_categories(cat);
+ }
+ }
+ }
struct PerfettoTracingSessionImpl* ts =
- PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS);
+ PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM);
- PerfettoTracingSessionSetup(ts, ser.get(), cfg_size);
+ std::string trace_config_string;
+ trace_config.SerializeToString(&trace_config_string);
+ PerfettoTracingSessionSetup(ts, trace_config_string.data(), trace_config_string.length());
+
+ // Fails to start here
PerfettoTracingSessionStartBlocking(ts);
return TracingSession::Adopt(ts);
diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h
index 4353554..8edb414 100644
--- a/libs/tracing_perfetto/tests/utils.h
+++ b/libs/tracing_perfetto/tests/utils.h
@@ -74,10 +74,6 @@
class Builder {
public:
Builder() = default;
- Builder& set_data_source_name(std::string data_source_name) {
- data_source_name_ = std::move(data_source_name);
- return *this;
- }
Builder& add_enabled_category(std::string category) {
enabled_categories_.push_back(std::move(category));
return *this;
@@ -86,12 +82,21 @@
disabled_categories_.push_back(std::move(category));
return *this;
}
+ Builder& add_atrace_category(std::string category) {
+ atrace_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_atrace_category_prefer_sdk(std::string category) {
+ atrace_categories_prefer_sdk_.push_back(std::move(category));
+ return *this;
+ }
TracingSession Build();
private:
- std::string data_source_name_;
std::vector<std::string> enabled_categories_;
std::vector<std::string> disabled_categories_;
+ std::vector<std::string> atrace_categories_;
+ std::vector<std::string> atrace_categories_prefer_sdk_;
};
static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
index 6f716ee..fc5336d 100644
--- a/libs/tracing_perfetto/tracing_perfetto.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -28,116 +28,112 @@
internal::registerWithPerfetto(test);
}
-Result traceBegin(uint64_t category, const char* name) {
+void traceBegin(uint64_t category, const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceBegin(*perfettoTeCategory, name);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_begin(category, name);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceBegin(*perfettoTeCategory, name);
}
}
-Result traceEnd(uint64_t category) {
+void traceEnd(uint64_t category) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceEnd(*perfettoTeCategory);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_end(category);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceEnd(*perfettoTeCategory);
}
}
-Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) {
+void traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_begin(category, name, cookie);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie);
}
}
-Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) {
+void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_end(category, name, cookie);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie);
}
}
-Result traceAsyncBeginForTrack(uint64_t category, const char* name,
+void traceAsyncBeginForTrack(uint64_t category, const char* name,
const char* trackName, int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_for_track_begin(category, trackName, name, cookie);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie);
}
}
-Result traceAsyncEndForTrack(uint64_t category, const char* trackName,
+void traceAsyncEndForTrack(uint64_t category, const char* trackName,
int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_for_track_end(category, trackName, cookie);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie);
}
}
-Result traceInstant(uint64_t category, const char* name) {
+void traceInstant(uint64_t category, const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceInstant(*perfettoTeCategory, name);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_instant(category, name);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceInstant(*perfettoTeCategory, name);
}
}
-Result traceInstantForTrack(uint64_t category, const char* trackName,
+void traceInstantForTrack(uint64_t category, const char* trackName,
const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_instant_for_track(category, trackName, name);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name);
}
}
-Result traceCounter(uint64_t category, const char* name, int64_t value) {
+void traceCounter(uint64_t category, const char* name, int64_t value) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return internal::perfettoTraceCounter(*perfettoTeCategory, name, value);
- } else {
+
+ if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_int64(category, name, value);
- return Result::SUCCESS;
+ } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+ internal::perfettoTraceCounter(*perfettoTeCategory, name, value);
}
}
bool isTagEnabled(uint64_t category) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- if (perfettoTeCategory != nullptr) {
- return true;
- } else {
- return (atrace_get_enabled_tags() & category) != 0;
- }
+ return internal::isPerfettoCategoryEnabled(perfettoTeCategory)
+ || atrace_is_tag_enabled(category);
}
-
} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index 758ace6..9a0042a 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -44,13 +44,13 @@
C(rro, "rro", "RRO category") \
C(thermal, "thermal", "Thermal category")
-#include "tracing_perfetto_internal.h"
-
-#include <inttypes.h>
-
+#include <atomic>
#include <mutex>
#include <android_os.h>
+#include <android-base/properties.h>
+#include <cutils/trace.h>
+#include <inttypes.h>
#include "perfetto/public/compiler.h"
#include "perfetto/public/producer.h"
@@ -58,19 +58,42 @@
#include "perfetto/public/te_macros.h"
#include "perfetto/public/track_event.h"
#include "trace_categories.h"
-#include "trace_result.h"
+#include "tracing_perfetto_internal.h"
+
+#ifdef __BIONIC__
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+#endif
namespace tracing_perfetto {
namespace internal {
namespace {
-
PERFETTO_TE_CATEGORIES_DECLARE(FRAMEWORK_CATEGORIES);
PERFETTO_TE_CATEGORIES_DEFINE(FRAMEWORK_CATEGORIES);
-std::atomic_bool is_perfetto_registered = false;
+static constexpr char kPreferFlagProperty[] = "debug.atrace.prefer_sdk";
+static std::atomic<const prop_info*> prefer_property_info = nullptr;
+static std::atomic_uint32_t last_prefer_seq_num = 0;
+static std::atomic_uint64_t prefer_flags = 0;
+
+static const prop_info* system_property_find(const char* name [[maybe_unused]]) {
+ #ifdef __BIONIC__
+ return __system_property_find(name);
+ #endif
+
+ return nullptr;
+}
+
+static uint32_t system_property_serial(const prop_info* pi [[maybe_unused]]) {
+ #ifdef __BIONIC__
+ return __system_property_serial(pi);
+ #endif
+
+ return last_prefer_seq_num;
+}
struct PerfettoTeCategory* toCategory(uint64_t inCategory) {
switch (inCategory) {
@@ -137,8 +160,60 @@
} // namespace
-bool isPerfettoRegistered() {
- return is_perfetto_registered;
+bool isPerfettoCategoryEnabled(PerfettoTeCategory* category) {
+ return category != nullptr;
+}
+
+/**
+ * Updates the cached |prefer_flags|.
+ *
+ * We cache the prefer_flags because reading it on every trace event is expensive.
+ * The cache is invalidated when a sys_prop sequence number changes.
+ */
+void updatePreferFlags() {
+ if (!prefer_property_info.load(std::memory_order_acquire)) {
+ auto* new_prefer_property_info = system_property_find(kPreferFlagProperty);
+ prefer_flags.store(android::base::GetIntProperty(kPreferFlagProperty, 0),
+ std::memory_order_relaxed);
+
+ if (!new_prefer_property_info) {
+ // This should never happen. If it does, we fail gracefully and end up reading the property
+ // traced event.
+ return;
+ }
+
+ last_prefer_seq_num = system_property_serial(new_prefer_property_info);
+ prefer_property_info.store(new_prefer_property_info, std::memory_order_release);
+ }
+
+ uint32_t prefer_seq_num = system_property_serial(prefer_property_info);
+ if (prefer_seq_num != last_prefer_seq_num.load(std::memory_order_acquire)) {
+ prefer_flags.store(android::base::GetIntProperty(kPreferFlagProperty, 0),
+ std::memory_order_relaxed);
+ last_prefer_seq_num.store(prefer_seq_num, std::memory_order_release);
+ }
+}
+
+bool shouldPreferAtrace(PerfettoTeCategory *perfettoCategory, uint64_t atraceCategory) {
+ // There are 3 cases:
+ // 1. Atrace is not enabled.
+ if (!atrace_is_tag_enabled(atraceCategory)) {
+ return false;
+ }
+
+ // 2. Atrace is enabled but perfetto is not enabled.
+ if (!isPerfettoCategoryEnabled(perfettoCategory)) {
+ return true;
+ }
+
+ // Update prefer_flags before checking it below
+ updatePreferFlags();
+
+ // 3. Atrace and perfetto are enabled.
+ // Even though this category is enabled for track events, the config mandates that we downgrade
+ // it to atrace if the same atrace category is currently enabled. This prevents missing the
+ // event from a concurrent session that needs the same category in atrace.
+ return (atraceCategory & prefer_flags.load(std::memory_order_relaxed)) == 0;
}
struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) {
@@ -148,7 +223,7 @@
}
bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
- (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
+ (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
return enabled ? perfettoCategory : nullptr;
}
@@ -164,70 +239,57 @@
PerfettoProducerInit(args);
PerfettoTeInit();
PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES);
- is_perfetto_registered = true;
});
}
-Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) {
+void perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) {
PERFETTO_TE(category, PERFETTO_TE_SLICE_BEGIN(name));
- return Result::SUCCESS;
}
-Result perfettoTraceEnd(const struct PerfettoTeCategory& category) {
+void perfettoTraceEnd(const struct PerfettoTeCategory& category) {
PERFETTO_TE(category, PERFETTO_TE_SLICE_END());
- return Result::SUCCESS;
}
-Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
const char* trackName, uint64_t cookie) {
PERFETTO_TE(
category, PERFETTO_TE_SLICE_BEGIN(name),
PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
- return Result::SUCCESS;
}
-Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
+void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
const char* trackName, uint64_t cookie) {
PERFETTO_TE(
category, PERFETTO_TE_SLICE_END(),
PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
- return Result::SUCCESS;
}
-Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
uint64_t cookie) {
- return perfettoTraceAsyncBeginForTrack(category, name, name, cookie);
+ perfettoTraceAsyncBeginForTrack(category, name, name, cookie);
}
-Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
uint64_t cookie) {
- return perfettoTraceAsyncEndForTrack(category, name, cookie);
+ perfettoTraceAsyncEndForTrack(category, name, cookie);
}
-Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) {
+void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) {
PERFETTO_TE(category, PERFETTO_TE_INSTANT(name));
- return Result::SUCCESS;
}
-Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
+void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
const char* trackName, const char* name) {
PERFETTO_TE(
category, PERFETTO_TE_INSTANT(name),
PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid()));
- return Result::SUCCESS;
}
-Result perfettoTraceCounter(const struct PerfettoTeCategory& category,
+void perfettoTraceCounter(const struct PerfettoTeCategory& category,
[[maybe_unused]] const char* name, int64_t value) {
PERFETTO_TE(category, PERFETTO_TE_COUNTER(),
PERFETTO_TE_INT_COUNTER(value));
- return Result::SUCCESS;
}
-
-uint64_t getDefaultCategories() {
- return TRACE_CATEGORIES;
-}
-
} // namespace internal
} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h
index 79e4b8f..3e1ac2a 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.h
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.h
@@ -19,7 +19,6 @@
#include <stdint.h>
-#include "include/trace_result.h"
#include "perfetto/public/te_category_macros.h"
namespace tracing_perfetto {
@@ -32,31 +31,33 @@
void registerWithPerfetto(bool test = false);
-Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name);
+void perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name);
-Result perfettoTraceEnd(const struct PerfettoTeCategory& category);
+void perfettoTraceEnd(const struct PerfettoTeCategory& category);
-Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
uint64_t cookie);
-Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
uint64_t cookie);
-Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
const char* trackName, uint64_t cookie);
-Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
+void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
const char* trackName, uint64_t cookie);
-Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name);
+void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name);
-Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
+void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
const char* trackName, const char* name);
-Result perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name,
+void perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name,
int64_t value);
-uint64_t getDefaultCategories();
+bool isPerfettoCategoryEnabled(PerfettoTeCategory *perfettoTeCategory);
+
+bool shouldPreferAtrace(PerfettoTeCategory *perfettoTeCategory, uint64_t category);
} // namespace internal
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 00dd6ba..31fbb51 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -202,6 +202,7 @@
}
auto it = mMousePointersByDisplay.find(targetDisplay);
if (it != mMousePointersByDisplay.end()) {
+ mPolicy.notifyMouseCursorFadedOnTyping();
it->second->fade(PointerControllerInterface::Transition::GRADUAL);
}
}
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp
index a7c6d16..a365003 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.cpp
+++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp
@@ -68,7 +68,7 @@
InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime,
uint16_t vendorId, uint16_t productId,
- std::set<InputDeviceUsageSource> sources)
+ const std::set<InputDeviceUsageSource>& sources)
: isDown(isDown),
eventTime(eventTime),
readTime(readTime),
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
index e9deb2d..1756944 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.h
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -76,7 +76,7 @@
struct InputEventTimeline {
InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId,
- uint16_t productId, std::set<InputDeviceUsageSource> sources);
+ uint16_t productId, const std::set<InputDeviceUsageSource>& sources);
const bool isDown; // True if this is an ACTION_DOWN event
const nsecs_t eventTime;
const nsecs_t readTime;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 080281b..35519d1 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -397,6 +397,9 @@
* Returns ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID if no device has been used since boot.
*/
virtual DeviceId getLastUsedInputDeviceId() = 0;
+
+ /* Notifies that mouse cursor faded due to typing. */
+ virtual void notifyMouseCursorFadedOnTyping() = 0;
};
// --- TouchAffineTransformation ---
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 7a85c12..e1f8fda 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -58,6 +58,9 @@
/* Returns true if any InputConnection is currently active. */
virtual bool isInputMethodConnectionActive() = 0;
+
+ /* Notifies that mouse cursor faded due to typing. */
+ virtual void notifyMouseCursorFadedOnTyping() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index b2f15b4..a052a4e 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -78,7 +78,6 @@
name: "libinputreader_defaults",
srcs: [":libinputreader_sources"],
shared_libs: [
- "android.companion.virtualdevice.flags-aconfig-cc-host",
"libbase",
"libcap",
"libcrypto",
@@ -116,6 +115,7 @@
"libinputreader_defaults",
],
shared_libs: [
+ "android.companion.virtualdevice.flags-aconfig-cc-host",
"libinputflinger_base",
],
export_header_lib_headers: [
@@ -141,6 +141,7 @@
shared_libs: [
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
+ "android.companion.virtualdevice.flags-aconfig-cc-host",
"libinputflinger_base",
"libjsoncpp",
],
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index b7ace8b..9e9e816 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -916,6 +916,12 @@
return mLastUsedDeviceId;
}
+void InputReader::notifyMouseCursorFadedOnTyping() {
+ std::scoped_lock _l(mLock);
+ // disable touchpad taps when cursor has faded due to typing
+ mPreventingTouchpadTaps = true;
+}
+
void InputReader::dump(std::string& dump) {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 6f8c289..03ca840 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -118,6 +118,8 @@
DeviceId getLastUsedInputDeviceId() override;
+ void notifyMouseCursorFadedOnTyping() override;
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
@@ -199,7 +201,7 @@
std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
mDeviceToEventHubIdsMap GUARDED_BY(mLock);
- // true if tap-to-click on touchpad currently disabled
+ // true if tap-to-click on touchpad is currently disabled
bool mPreventingTouchpadTaps GUARDED_BY(mLock){false};
// records timestamp of the last key press on the physical keyboard
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 25f4893..4a21e48 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -493,12 +493,6 @@
void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) {
InputReaderContext& context = *getContext();
context.setLastKeyDownTimestamp(downTime);
- // Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard
- // shortcuts
- bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode);
- if (shouldHideCursor && context.getPolicy()->isInputMethodConnectionActive()) {
- context.setPreventingTouchpadTaps(true);
- }
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 20fd359..b72cc6e 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -27,11 +27,14 @@
namespace android {
+constexpr float kDefaultScaleFactor = 1.0f;
+
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
- : InputMapper(deviceContext, readerConfig), mOrientation(ui::ROTATION_0) {
- mSource = AINPUT_SOURCE_ROTARY_ENCODER;
-}
+ : InputMapper(deviceContext, readerConfig),
+ mSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ mScalingFactor(kDefaultScaleFactor),
+ mOrientation(ui::ROTATION_0) {}
RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
@@ -51,9 +54,10 @@
std::optional<float> scalingFactor = config.getFloat("device.scalingFactor");
if (!scalingFactor.has_value()) {
ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
- "default to 1.0!\n");
+ "default to %f!\n",
+ kDefaultScaleFactor);
}
- mScalingFactor = scalingFactor.value_or(1.0f);
+ mScalingFactor = scalingFactor.value_or(kDefaultScaleFactor);
info.addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
res.value_or(0.0f) * mScalingFactor);
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index beba3b8..984e217 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -977,8 +977,9 @@
viewportChanged = mViewport != newViewport;
}
+ const bool deviceModeChanged = mDeviceMode != oldDeviceMode;
bool skipViewportUpdate = false;
- if (viewportChanged) {
+ if (viewportChanged || deviceModeChanged) {
const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation;
const bool viewportDisplayIdChanged = mViewport.displayId != newViewport.displayId;
mViewport = newViewport;
@@ -1020,7 +1021,6 @@
}
// If moving between pointer modes, need to reset some state.
- bool deviceModeChanged = mDeviceMode != oldDeviceMode;
if (deviceModeChanged) {
mOrientedRanges.clear();
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index beab6e7..87b72af 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -339,8 +339,8 @@
int32_t buttonState{};
// Scroll state.
- int32_t rawVScroll{};
- int32_t rawHScroll{};
+ float rawVScroll{};
+ float rawHScroll{};
inline void clear() { *this = RawState(); }
};
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index daab636..5c5fd3f 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -258,11 +258,9 @@
// 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
mGestureInterpreter->SetPropProvider(const_cast<GesturesPropProvider*>(&gesturePropProvider),
&mPropertyProvider);
- if (input_flags::enable_gestures_library_timer_provider()) {
- mGestureInterpreter->SetTimerProvider(const_cast<GesturesTimerProvider*>(
- &kGestureTimerProvider),
- &mTimerProvider);
- }
+ mGestureInterpreter->SetTimerProvider(const_cast<GesturesTimerProvider*>(
+ &kGestureTimerProvider),
+ &mTimerProvider);
mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
}
@@ -300,12 +298,8 @@
dump += addLinePrefix(mGestureConverter.dump(), INDENT4);
dump += INDENT3 "Gesture properties:\n";
dump += addLinePrefix(mPropertyProvider.dump(), INDENT4);
- if (input_flags::enable_gestures_library_timer_provider()) {
- dump += INDENT3 "Timer provider:\n";
- dump += addLinePrefix(mTimerProvider.dump(), INDENT4);
- } else {
- dump += INDENT3 "Timer provider: disabled by flag\n";
- }
+ dump += INDENT3 "Timer provider:\n";
+ dump += addLinePrefix(mTimerProvider.dump(), INDENT4);
dump += INDENT3 "Captured event converter:\n";
dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
dump += StringPrintf(INDENT3 "DisplayId: %s\n",
@@ -468,9 +462,6 @@
}
std::list<NotifyArgs> TouchpadInputMapper::timeoutExpired(nsecs_t when) {
- if (!input_flags::enable_gestures_library_timer_provider()) {
- return {};
- }
mTimerProvider.triggerCallbacks(when);
return processGestures(when, when);
}
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
index f85cab2..5373440 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
@@ -16,18 +16,29 @@
#include "CursorScrollAccumulator.h"
+#include <android_companion_virtualdevice_flags.h>
#include "EventHub.h"
#include "InputDevice.h"
namespace android {
-CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) {
+namespace vd_flags = android::companion::virtualdevice::flags;
+
+CursorScrollAccumulator::CursorScrollAccumulator()
+ : mHaveRelWheel(false),
+ mHaveRelHWheel(false),
+ mHaveRelWheelHighRes(false),
+ mHaveRelHWheelHighRes(false) {
clearRelativeAxes();
}
void CursorScrollAccumulator::configure(InputDeviceContext& deviceContext) {
mHaveRelWheel = deviceContext.hasRelativeAxis(REL_WHEEL);
mHaveRelHWheel = deviceContext.hasRelativeAxis(REL_HWHEEL);
+ if (vd_flags::high_resolution_scroll()) {
+ mHaveRelWheelHighRes = deviceContext.hasRelativeAxis(REL_WHEEL_HI_RES);
+ mHaveRelHWheelHighRes = deviceContext.hasRelativeAxis(REL_HWHEEL_HI_RES);
+ }
}
void CursorScrollAccumulator::reset(InputDeviceContext& deviceContext) {
@@ -42,11 +53,31 @@
void CursorScrollAccumulator::process(const RawEvent& rawEvent) {
if (rawEvent.type == EV_REL) {
switch (rawEvent.code) {
+ case REL_WHEEL_HI_RES:
+ if (mHaveRelWheelHighRes) {
+ mRelWheel =
+ rawEvent.value / static_cast<float>(kEvdevHighResScrollUnitsPerDetent);
+ }
+ break;
+ case REL_HWHEEL_HI_RES:
+ if (mHaveRelHWheelHighRes) {
+ mRelHWheel =
+ rawEvent.value / static_cast<float>(kEvdevHighResScrollUnitsPerDetent);
+ }
+ break;
case REL_WHEEL:
- mRelWheel = rawEvent.value;
+ // We should ignore regular scroll events, if we have already have high-res scroll
+ // enabled.
+ if (!mHaveRelWheelHighRes) {
+ mRelWheel = rawEvent.value;
+ }
break;
case REL_HWHEEL:
- mRelHWheel = rawEvent.value;
+ // We should ignore regular scroll events, if we have already have high-res scroll
+ // enabled.
+ if (!mHaveRelHWheelHighRes) {
+ mRelHWheel = rawEvent.value;
+ }
break;
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
index e563620..d3373cc 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -16,15 +16,12 @@
#pragma once
-#include <stdint.h>
-
namespace android {
class InputDeviceContext;
struct RawEvent;
/* Keeps track of cursor scrolling motions. */
-
class CursorScrollAccumulator {
public:
CursorScrollAccumulator();
@@ -37,19 +34,17 @@
inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
inline bool haveRelativeHWheel() const { return mHaveRelHWheel; }
- inline int32_t getRelativeX() const { return mRelX; }
- inline int32_t getRelativeY() const { return mRelY; }
- inline int32_t getRelativeVWheel() const { return mRelWheel; }
- inline int32_t getRelativeHWheel() const { return mRelHWheel; }
+ inline float getRelativeVWheel() const { return mRelWheel; }
+ inline float getRelativeHWheel() const { return mRelHWheel; }
private:
bool mHaveRelWheel;
bool mHaveRelHWheel;
+ bool mHaveRelWheelHighRes;
+ bool mHaveRelHWheelHighRes;
- int32_t mRelX;
- int32_t mRelY;
- int32_t mRelWheel;
- int32_t mRelHWheel;
+ float mRelWheel;
+ float mRelHWheel;
void clearRelativeAxes();
};
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 189f117..65e0429 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -109,6 +109,7 @@
},
},
static_libs: [
+ "android.companion.virtualdevice.flags-aconfig-cc-test",
"libflagtest",
"libgmock",
],
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 83074ff..727237f 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -22,6 +22,7 @@
#include <variant>
#include <android-base/logging.h>
+#include <android_companion_virtualdevice_flags.h>
#include <com_android_input_flags.h>
#include <gtest/gtest.h>
#include <input/DisplayViewport.h>
@@ -127,6 +128,7 @@
} // namespace
namespace input_flags = com::android::input::flags;
+namespace vd_flags = android::companion::virtualdevice::flags;
/**
* Unit tests for CursorInputMapper.
@@ -151,6 +153,10 @@
.WillRepeatedly(Return(false));
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL))
.WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(false));
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
@@ -194,6 +200,7 @@
protected:
void SetUp() override {
input_flags::enable_new_mouse_pointer_ballistics(false);
+ vd_flags::high_resolution_scroll(false);
CursorInputMapperUnitTestBase::SetUp();
}
};
@@ -840,6 +847,72 @@
WithOrientation(0.0f), WithDistance(0.0f)))));
}
+TEST_F(CursorInputMapperUnitTest, ProcessRegularScroll) {
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(1.0f, 1.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessHighResScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, 0.5f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, HighResScrollIgnoresRegularScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, 0.5f)))));
+}
+
/**
* When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
* pointer acceleration or speed processing should not be applied.
@@ -1030,6 +1103,72 @@
WithRelativeMotion(10, 20)))));
}
+TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessRegularScroll) {
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(1.0f, 1.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessHighResScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, 0.5f)))));
+}
+
+TEST_F(CursorInputMapperUnitTestWithNewBallistics, HighResScrollIgnoresRegularScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createMapper();
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(0.5f, 0.5f)))));
+}
+
namespace {
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 2d50c37..1ba79a9 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -56,6 +56,7 @@
using namespace ftl::flag_operators;
using testing::AllOf;
+using testing::VariantWith;
using std::chrono_literals::operator""ms;
using std::chrono_literals::operator""s;
@@ -4492,15 +4493,15 @@
void prepareButtons();
void prepareAxes(int axes);
- void processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
- void processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
- void processUp(SingleTouchInputMapper& mappery);
- void processPressure(SingleTouchInputMapper& mapper, int32_t pressure);
- void processToolMajor(SingleTouchInputMapper& mapper, int32_t toolMajor);
- void processDistance(SingleTouchInputMapper& mapper, int32_t distance);
- void processTilt(SingleTouchInputMapper& mapper, int32_t tiltX, int32_t tiltY);
- void processKey(SingleTouchInputMapper& mapper, int32_t code, int32_t value);
- void processSync(SingleTouchInputMapper& mapper);
+ std::list<NotifyArgs> processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
+ std::list<NotifyArgs> processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
+ std::list<NotifyArgs> processUp(SingleTouchInputMapper& mappery);
+ std::list<NotifyArgs> processPressure(SingleTouchInputMapper& mapper, int32_t pressure);
+ std::list<NotifyArgs> processToolMajor(SingleTouchInputMapper& mapper, int32_t toolMajor);
+ std::list<NotifyArgs> processDistance(SingleTouchInputMapper& mapper, int32_t distance);
+ std::list<NotifyArgs> processTilt(SingleTouchInputMapper& mapper, int32_t tiltX, int32_t tiltY);
+ std::list<NotifyArgs> processKey(SingleTouchInputMapper& mapper, int32_t code, int32_t value);
+ std::list<NotifyArgs> processSync(SingleTouchInputMapper& mapper);
};
void SingleTouchInputMapperTest::prepareButtons() {
@@ -4530,47 +4531,57 @@
}
}
-void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processDown(SingleTouchInputMapper& mapper,
+ int32_t x, int32_t y) {
+ std::list<NotifyArgs> args;
+ args += process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
+ args += process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
+ args += process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
+ return args;
}
-void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processMove(SingleTouchInputMapper& mapper,
+ int32_t x, int32_t y) {
+ std::list<NotifyArgs> args;
+ args += process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
+ args += process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
+ return args;
}
-void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 0);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) {
+ return process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 0);
}
-void SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_PRESSURE, pressure);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper,
+ int32_t pressure) {
+ return process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_PRESSURE, pressure);
}
-void SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper,
- int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper,
+ int32_t toolMajor) {
+ return process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
}
-void SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_DISTANCE, distance);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper,
+ int32_t distance) {
+ return process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_DISTANCE, distance);
}
-void SingleTouchInputMapperTest::processTilt(SingleTouchInputMapper& mapper, int32_t tiltX,
- int32_t tiltY) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_X, tiltX);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_Y, tiltY);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processTilt(SingleTouchInputMapper& mapper,
+ int32_t tiltX, int32_t tiltY) {
+ std::list<NotifyArgs> args;
+ args += process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_X, tiltX);
+ args += process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_Y, tiltY);
+ return args;
}
-void SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper, int32_t code,
- int32_t value) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper,
+ int32_t code, int32_t value) {
+ return process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
}
-void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+std::list<NotifyArgs> SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) {
+ return process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
}
TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
@@ -4661,6 +4672,42 @@
ASSERT_FALSE(flags[1]);
}
+TEST_F(SingleTouchInputMapperTest, DeviceTypeChange_RecalculatesRawToDisplayTransform) {
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
+
+ const int32_t x = 900;
+ const int32_t y = 75;
+ std::list<NotifyArgs> args;
+ args += processDown(mapper, x, y);
+ args += processSync(mapper);
+
+ // Assert that motion event is received in display coordinate space for deviceType touchScreen.
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(toDisplayX(x), toDisplayY(y))))));
+
+ // Add device type association after the device was created.
+ mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
+ // Send update to the mapper.
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::Change::DEVICE_TYPE /*changes*/);
+
+ args.clear();
+ args += processDown(mapper, x, y);
+ args += processSync(mapper);
+
+ // Assert that motion event is received in raw coordinate space for deviceType touchNavigation.
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(x - RAW_X_MIN, y - RAW_Y_MIN)))));
+}
+
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index bacc6d4..48e0b4f 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -187,6 +187,7 @@
MOCK_METHOD(void, notifyPointerDisplayIdChanged,
(ui::LogicalDisplayId displayId, const FloatPoint& position), (override));
MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
+ MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override));
};
} // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index ab47cc6..d3e8dee 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -69,48 +69,8 @@
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
AINPUT_SOURCE_KEYBOARD);
}
-
- void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes,
- const bool expectPrevent) {
- if (expectPrevent) {
- EXPECT_CALL(mMockInputReaderContext, setPreventingTouchpadTaps(true))
- .Times(keyCodes.size());
- }
- for (int32_t keyCode : keyCodes) {
- process(EV_KEY, keyCode, 1);
- process(EV_SYN, SYN_REPORT, 0);
- process(EV_KEY, keyCode, 0);
- process(EV_SYN, SYN_REPORT, 0);
- }
- }
};
-/**
- * Touchpad tap should not be disabled if there is no active Input Method Connection
- */
-TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) {
- testTouchpadTapStateForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectPrevent= */ false);
-}
-
-/**
- * Touchpad tap should be disabled if there is a active Input Method Connection
- */
-TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionDisableTouchpadTap) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- testTouchpadTapStateForKeys({KEY_0, KEY_A}, /* expectPrevent= */ true);
-}
-
-/**
- * Touchpad tap should not be disabled by meta keys even if Input Method Connection is active
- */
-TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTouchpadTap) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
- KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA,
- KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK};
- testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false);
-}
-
TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) {
nsecs_t when = ARBITRARY_TIME;
std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT};
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 9a5b6a7..144e723 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -2294,7 +2294,13 @@
assertPointerControllerRemoved(pc);
}
-class PointerVisibilityOnKeyPressTest : public PointerChoreographerTest {
+using PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam =
+ std::tuple<std::string_view /*name*/, uint32_t /*source*/>;
+
+class PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture
+ : public PointerChoreographerTest,
+ public testing::WithParamInterface<
+ PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam> {
protected:
const std::unordered_map<int32_t, int32_t>
mMetaKeyStates{{AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON},
@@ -2358,15 +2364,28 @@
}
};
-TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutImeConnectionDoesNotHidePointer) {
+INSTANTIATE_TEST_SUITE_P(
+ PointerChoreographerTest, PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ testing::Values(std::make_tuple("Mouse", AINPUT_SOURCE_MOUSE),
+ std::make_tuple("Touchpad", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD)),
+ [](const testing::TestParamInfo<
+ PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixtureParam>& p) {
+ return std::string{std::get<0>(p.param)};
+ });
+
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ KeystrokesWithoutImeConnectionDoesNotHidePointerOrDisablesTouchpadTap) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0);
+
notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0);
notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A);
notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_CTRL_LEFT);
@@ -2374,16 +2393,19 @@
ASSERT_TRUE(pc->isPointerShown());
}
-TEST_F(PointerVisibilityOnKeyPressTest, AlphanumericKeystrokesWithImeConnectionHidePointer) {
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ AlphanumericKeystrokesWithImeConnectionHidePointerAndDisablesTouchpadTap) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(2);
notifyKey(DISPLAY_ID, AKEYCODE_0);
ASSERT_FALSE(pc->isPointerShown());
@@ -2394,17 +2416,19 @@
ASSERT_FALSE(pc->isPointerShown());
}
-TEST_F(PointerVisibilityOnKeyPressTest, MetaKeystrokesDoNotHidePointer) {
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ MetaKeystrokesDoNotHidePointerOrDisablesTouchpadTap) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0,
- {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(SECOND_DEVICE_ID, source, DISPLAY_ID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0);
const std::vector<int32_t> metaKeyCodes{AKEYCODE_ALT_LEFT, AKEYCODE_ALT_RIGHT,
AKEYCODE_SHIFT_LEFT, AKEYCODE_SHIFT_RIGHT,
@@ -2420,14 +2444,16 @@
ASSERT_TRUE(pc->isPointerShown());
}
-TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplay) {
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture,
+ KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplayAndDisablesTouchpadTap) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
mChoreographer.setFocusedDisplay(DISPLAY_ID);
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID),
+ {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
auto pc1 = assertPointerControllerCreated(ControllerType::MOUSE);
auto pc2 = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -2435,6 +2461,7 @@
ASSERT_TRUE(pc2->isPointerShown());
EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(2);
notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0);
ASSERT_FALSE(pc1->isPointerShown());
@@ -2446,16 +2473,19 @@
ASSERT_TRUE(pc2->isPointerShown());
}
-TEST_F(PointerVisibilityOnKeyPressTest, TestMetaKeyCombinations) {
+TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, TestMetaKeyCombinations) {
+ const auto& [_, source] = GetParam();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Mouse connected
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+
EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
- // meta key combinations that should hide pointer
+ // meta key combinations that should hide pointer and disable touchpad taps
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(5);
metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_LEFT);
metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_RIGHT);
metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_CAPS_LOCK);
@@ -2463,6 +2493,7 @@
metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SCROLL_LOCK);
// meta key combinations that should not hide pointer
+ EXPECT_CALL(mMockPolicy, notifyMouseCursorFadedOnTyping).Times(0);
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_LEFT);
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_RIGHT);
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_LEFT);
diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
index 94cfc32..366b3dc 100644
--- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
+++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
@@ -22,6 +22,7 @@
#include <variant>
#include <android-base/logging.h>
+#include <android_companion_virtualdevice_flags.h>
#include <gtest/gtest.h>
#include <input/DisplayViewport.h>
#include <linux/input-event-codes.h>
@@ -109,6 +110,8 @@
} // namespace
+namespace vd_flags = android::companion::virtualdevice::flags;
+
/**
* Unit tests for RotaryEncoderInputMapper.
*/
@@ -122,6 +125,10 @@
.WillRepeatedly(Return(true));
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL))
.WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
+ .WillRepeatedly(Return(false));
}
};
@@ -166,4 +173,53 @@
WithDisplayId(ui::LogicalDisplayId::INVALID)))));
}
+TEST_F(RotaryEncoderInputMapperTest, ProcessRegularScroll) {
+ createDevice();
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(1.0f)))));
+}
+
+TEST_F(RotaryEncoderInputMapperTest, ProcessHighResScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createDevice();
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
+}
+
+TEST_F(RotaryEncoderInputMapperTest, HighResScrollIgnoresRegularScroll) {
+ vd_flags::high_resolution_scroll(true);
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
+ .WillRepeatedly(Return(true));
+ createDevice();
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index 65fb9c6..f8e3c22 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -697,6 +697,21 @@
return argDistance == distance;
}
+MATCHER_P(WithScroll, scroll, "InputEvent with specified scroll value") {
+ const auto argScroll = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SCROLL);
+ *result_listener << "expected scroll value " << scroll << ", but got " << argScroll;
+ return argScroll == scroll;
+}
+
+MATCHER_P2(WithScroll, scrollX, scrollY, "InputEvent with specified scroll values") {
+ const auto argScrollX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_HSCROLL);
+ const auto argScrollY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_VSCROLL);
+ *result_listener << "expected scroll values " << scrollX << " scroll x " << scrollY
+ << " scroll y, but got " << argScrollX << " scroll x " << argScrollY
+ << " scroll y";
+ return argScrollX == scrollX && argScrollY == scrollY;
+}
+
MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 7d26a43..d552c19 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -169,6 +169,8 @@
DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); }
+ void notifyMouseCursorFadedOnTyping() override { reader->notifyMouseCursorFadedOnTyping(); }
+
private:
std::unique_ptr<InputReaderInterface> reader;
};
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 7dccd69..d986b31 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -348,8 +348,8 @@
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
void notifyConfigurationChanged(nsecs_t) {}
- void setPreventingTouchpadTaps(bool prevent) {}
- bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
+ void setPreventingTouchpadTaps(bool prevent) override {}
+ bool isPreventingTouchpadTaps() override { return mFdp->ConsumeBool(); };
void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; };
nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; };
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index f339d41..4424a04 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -260,10 +260,6 @@
void DisplayColorProfile::populateColorModes(
const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes) {
- if (!hasWideColorGamut()) {
- return;
- }
-
// collect all known SDR render intents
std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
sSdrRenderIntents.end());
@@ -352,13 +348,9 @@
*outMode = iter->second.colorMode;
*outIntent = iter->second.renderIntent;
} else {
- // this is unexpected on a WCG display
- if (hasWideColorGamut()) {
- ALOGE("map unknown (%s)/(%s) to default color mode",
- dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
- decodeRenderIntent(intent).c_str());
- }
-
+ ALOGI("map unknown (%s)/(%s) to default color mode",
+ dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+ decodeRenderIntent(intent).c_str());
*outDataspace = Dataspace::UNKNOWN;
*outMode = ColorMode::NATIVE;
*outIntent = RenderIntent::COLORIMETRIC;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index 03a97dc..c354e4a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -123,10 +123,10 @@
.build();
}
- static impl::DisplayColorProfile createProfileWithSRGBColorModeSupport() {
+ static impl::DisplayColorProfile createProfileWithSRGBColorModeSupport(bool wcg = true) {
return ProfileFactory()
- .setHasWideColorGamut(true)
.addHdrType(Hdr::HDR10)
+ .setHasWideColorGamut(wcg)
.addColorModeRenderIntent(ColorMode::SRGB, RenderIntent::COLORIMETRIC)
.addColorModeRenderIntent(ColorMode::SRGB, RenderIntent::ENHANCE)
.addColorModeRenderIntent(ColorMode::SRGB, VendorRenderIntent)
@@ -289,7 +289,7 @@
TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasNoSupport) {
auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
- EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+ EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
@@ -306,6 +306,16 @@
EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
}
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasSRGBSupport_NoWCG) {
+ auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport(false);
+
+ EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+ EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+ EXPECT_TRUE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasSRGBSupport) {
auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
@@ -476,6 +486,40 @@
checkGetBestColorMode(profile, expectedResults);
}
+TEST_F(DisplayColorProfileTest,
+ getBestColorModeReturnsExpectedModesWhenOutputHasSRGBSupport_NoWCG) {
+ auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport(false);
+
+ // Note: This table of expected values goes with the table of arguments
+ // used in checkGetBestColorMode.
+ using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+ std::array<Result, 15> expectedResults = {
+ /* clang-format off */
+ /* 0 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 1 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+ /* 2 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+ /* 3 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 4 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+ /* 5 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+ /* 6 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 7 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+ /* 8 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+ /* 9 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 10 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 12 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 13 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* clang-format on */
+ };
+
+ checkGetBestColorMode(profile, expectedResults);
+}
+
TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasSRGBSupport) {
auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 8288b99..75b07a8 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -468,6 +468,12 @@
return false;
}
+void DisplayDevice::onVrrIdle(bool idle) {
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->onVrrIdle(idle);
+ }
+}
+
void DisplayDevice::animateOverlay() {
if (mRefreshRateOverlay) {
mRefreshRateOverlay->animate();
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3cc8cf5..1b8a3a8 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -196,6 +196,7 @@
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
void animateOverlay();
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
+ void onVrrIdle(bool idle);
// Enables an overlay to be display with the hdr/sdr ratio
void enableHdrSdrRatioOverlay(bool enable) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index da56014..334c104 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -211,7 +211,7 @@
return;
}
SFTRACE_CALL();
- if (sTraceHintSessionData) ATRACE_INT("Session hint", static_cast<int>(hint));
+ if (sTraceHintSessionData) SFTRACE_INT("Session hint", static_cast<int>(hint));
{
std::scoped_lock lock(mHintSessionMutex);
if (!ensurePowerHintSessionRunning()) {
@@ -298,7 +298,7 @@
SFTRACE_CALL();
{
mTargetDuration = targetDuration;
- if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
+ if (sTraceHintSessionData) SFTRACE_INT64("Time target", targetDuration.ns());
if (targetDuration == mLastTargetDurationSent) return;
std::scoped_lock lock(mHintSessionMutex);
if (!ensurePowerHintSessionRunning()) {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index bc4a41b..1076b2b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -300,7 +300,7 @@
bool mSessionConfigSupported = true;
bool mFirstConfigSupportCheck = true;
- // Whether we should emit ATRACE_INT data for hint sessions
+ // Whether we should emit SFTRACE_INT data for hint sessions
static const bool sTraceHintSessionData;
// Default target duration for the hint session
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 5550565..2a0ee5a 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -359,7 +359,11 @@
void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) {
std::scoped_lock lock(mMutex);
- mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
+ if (CC_UNLIKELY(acquireFenceTime == Fence::SIGNAL_TIME_PENDING)) {
+ mActuals.endTime = mActualQueueTime;
+ } else {
+ mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
+ }
}
void SurfaceFrame::setDropTime(nsecs_t dropTime) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 86c7d16..a6d2b15 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3060,6 +3060,8 @@
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
mDrawingState.acquireFence);
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
decrementPendingBufferCount();
if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
@@ -3238,7 +3240,7 @@
return static_cast<nsecs_t>(0);
}();
- if (ATRACE_ENABLED() && presentTime > 0) {
+ if (SFTRACE_ENABLED() && presentTime > 0) {
const auto presentIn = TimePoint::fromNs(presentTime) - TimePoint::now();
SFTRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d3b56f8..52f169e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1265,7 +1265,7 @@
const std::string mBlastTransactionName{"BufferTX - " + mName};
// This integer is incremented everytime a buffer arrives at the server for this layer,
// and decremented when a buffer is dropped or latched. When changed the integer is exported
- // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is
+ // to systrace with SFTRACE_INT and mBlastTransactionName. This way when debugging perf it is
// possible to see when a buffer arrived at the server, and in which frame it latched.
//
// You can understand the trace this way:
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 496033b..30b8eee 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -266,6 +266,7 @@
perfetto::protos::LayersProto LayerProtoFromSnapshotGenerator::generate(
const frontend::LayerHierarchy& root) {
mLayersProto.clear_layers();
+ mVisitedLayers.clear();
std::unordered_set<uint64_t> stackIdsToSkip;
if ((mTraceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) {
for (const auto& [layerStack, displayInfo] : mDisplayInfos) {
@@ -326,6 +327,10 @@
perfetto::protos::LayerProto* layerProto = mLayersProto.add_layers();
const frontend::RequestedLayerState& layer = *root.getLayer();
frontend::LayerSnapshot* snapshot = getSnapshot(path, layer);
+ if (mVisitedLayers.find(snapshot->uniqueSequence) != mVisitedLayers.end()) {
+ TransactionTraceWriter::getInstance().invoke("DuplicateLayer", /* overwrite= */ false);
+ return;
+ }
LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags);
for (const auto& [child, variant] : root.mChildren) {
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 20c2260..d672012 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -101,6 +101,8 @@
const frontend::DisplayInfos& mDisplayInfos;
uint32_t mTraceFlags;
perfetto::protos::LayersProto mLayersProto;
+ std::unordered_set<uint32_t> mVisitedLayers;
+
// winscope expects all the layers, so provide a snapshot even if it not currently drawing
std::unordered_map<frontend::LayerHierarchy::TraversalPath, frontend::LayerSnapshot,
frontend::LayerHierarchy::TraversalPathHash>
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9527a99..35f12a0 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -28,10 +28,11 @@
namespace android {
-auto RefreshRateOverlay::draw(int refreshRate, int renderFps, SkColor color,
+auto RefreshRateOverlay::draw(int refreshRate, int renderFps, bool idle, SkColor color,
ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
-> Buffers {
const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
+ const bool isSetByHwc = features.test(Features::SetByHwc);
Buffers buffers;
buffers.reserve(loopCount);
@@ -71,7 +72,11 @@
canvas->setMatrix(canvasTransform);
int left = 0;
- drawNumber(refreshRate, left, color, *canvas);
+ if (idle && !isSetByHwc) {
+ drawDash(left, *canvas);
+ } else {
+ drawNumber(refreshRate, left, color, *canvas);
+ }
left += 3 * (kDigitWidth + kDigitSpace);
if (features.test(Features::Spinner)) {
switch (i) {
@@ -104,7 +109,11 @@
left += kDigitWidth + kDigitSpace;
if (features.test(Features::RenderRate)) {
- drawNumber(renderFps, left, color, *canvas);
+ if (idle) {
+ drawDash(left, *canvas);
+ } else {
+ drawNumber(renderFps, left, color, *canvas);
+ }
}
left += 3 * (kDigitWidth + kDigitSpace);
@@ -138,6 +147,14 @@
SegmentDrawer::drawDigit(number % 10, left, color, canvas);
}
+void RefreshRateOverlay::drawDash(int left, SkCanvas& canvas) {
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
+
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
+}
+
std::unique_ptr<RefreshRateOverlay> RefreshRateOverlay::create(FpsRange range,
ftl::Flags<Features> features) {
std::unique_ptr<RefreshRateOverlay> overlay =
@@ -171,7 +188,8 @@
return mSurfaceControl != nullptr;
}
-auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps, bool idle)
+ -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
@@ -197,8 +215,8 @@
createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
- BufferCache::const_iterator it =
- mBufferCache.find({refreshRate.getIntValue(), renderFps.getIntValue(), transformHint});
+ BufferCache::const_iterator it = mBufferCache.find(
+ {refreshRate.getIntValue(), renderFps.getIntValue(), transformHint, idle});
if (it == mBufferCache.end()) {
const int maxFps = mFpsRange.max.getIntValue();
@@ -222,10 +240,10 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = draw(refreshIntFps, renderIntFps, color, transformHint, mFeatures);
+ auto buffers = draw(refreshIntFps, renderIntFps, idle, color, transformHint, mFeatures);
it = mBufferCache
- .try_emplace({refreshIntFps, renderIntFps, transformHint}, std::move(buffers))
- .first;
+ .try_emplace({refreshIntFps, renderIntFps, transformHint, idle},
+ std::move(buffers)).first;
}
return it->second;
@@ -257,7 +275,15 @@
void RefreshRateOverlay::changeRefreshRate(Fps refreshRate, Fps renderFps) {
mRefreshRate = refreshRate;
mRenderFps = renderFps;
- const auto buffer = getOrCreateBuffers(refreshRate, renderFps)[mFrame];
+ const auto buffer = getOrCreateBuffers(refreshRate, renderFps, mIsVrrIdle)[mFrame];
+ createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+}
+
+void RefreshRateOverlay::onVrrIdle(bool idle) {
+ mIsVrrIdle = idle;
+ if (!mRefreshRate || !mRenderFps) return;
+
+ const auto buffer = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle)[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
@@ -265,7 +291,7 @@
if (mFeatures.test(Features::RenderRate) && mRefreshRate &&
FlagManager::getInstance().misc1()) {
mRenderFps = renderFps;
- const auto buffer = getOrCreateBuffers(*mRefreshRate, renderFps)[mFrame];
+ const auto buffer = getOrCreateBuffers(*mRefreshRate, renderFps, mIsVrrIdle)[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
}
@@ -273,7 +299,7 @@
void RefreshRateOverlay::animate() {
if (!mFeatures.test(Features::Spinner) || !mRefreshRate) return;
- const auto& buffers = getOrCreateBuffers(*mRefreshRate, *mRenderFps);
+ const auto& buffers = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index b2896f0..d8aa048 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -57,6 +57,7 @@
void changeRenderRate(Fps);
void animate();
bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
+ void onVrrIdle(bool idle);
RefreshRateOverlay(ConstructorTag, FpsRange, ftl::Flags<Features>);
@@ -65,11 +66,12 @@
using Buffers = std::vector<sp<GraphicBuffer>>;
- static Buffers draw(int refreshRate, int renderFps, SkColor, ui::Transform::RotationFlags,
- ftl::Flags<Features>);
+ static Buffers draw(int refreshRate, int renderFps, bool idle, SkColor,
+ ui::Transform::RotationFlags, ftl::Flags<Features>);
static void drawNumber(int number, int left, SkColor, SkCanvas&);
+ static void drawDash(int left, SkCanvas&);
- const Buffers& getOrCreateBuffers(Fps, Fps);
+ const Buffers& getOrCreateBuffers(Fps, Fps, bool);
SurfaceComposerClient::Transaction createTransaction() const;
@@ -77,10 +79,11 @@
int refreshRate;
int renderFps;
ui::Transform::RotationFlags flags;
+ bool idle;
bool operator==(Key other) const {
return refreshRate == other.refreshRate && renderFps == other.renderFps &&
- flags == other.flags;
+ flags == other.flags && idle == other.idle;
}
};
@@ -89,6 +92,7 @@
std::optional<Fps> mRefreshRate;
std::optional<Fps> mRenderFps;
+ bool mIsVrrIdle = false;
size_t mFrame = 0;
const FpsRange mFpsRange; // For color interpolation.
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index 43cdb5e..f430526 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -33,6 +33,7 @@
virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
Fps renderRate) = 0;
virtual void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) = 0;
+ virtual void vrrDisplayIdle(bool idle) = 0;
protected:
~ISchedulerCallback() = default;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 0b17c84..9f6eab2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -634,11 +634,12 @@
// If all layers are category NoPreference, use the current config.
if (noPreferenceLayers + noVoteLayers == layers.size()) {
ALOGV("All layers NoPreference");
- const auto ascendingWithPreferred =
- rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId());
+ constexpr float kScore = std::numeric_limits<float>::max();
+ FrameRateRanking currentMode;
+ currentMode.emplace_back(ScoredFrameRate{getActiveModeLocked(), kScore});
SFTRACE_FORMAT_INSTANT("%s (All layers NoPreference)",
- to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str());
- return {ascendingWithPreferred, kNoSignals};
+ to_string(currentMode.front().frameRateMode.fps).c_str());
+ return {currentMode, kNoSignals};
}
const bool smoothSwitchOnly = categorySmoothSwitchOnlyLayers > 0;
@@ -1065,7 +1066,7 @@
ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
SFTRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__,
to_string(overrideFps).c_str(), uid);
- if (ATRACE_ENABLED() && FlagManager::getInstance().trace_frame_rate_override()) {
+ if (SFTRACE_ENABLED() && FlagManager::getInstance().trace_frame_rate_override()) {
std::stringstream ss;
ss << "FrameRateOverride " << uid;
SFTRACE_INT(ss.str().c_str(), overrideFps.getIntValue());
@@ -1500,7 +1501,7 @@
return str;
};
ALOGV("%s render rates: %s, isVrrDevice? %d", rangeName, stringifyModes().c_str(),
- mIsVrrDevice);
+ mIsVrrDevice.load());
return frameRateModes;
};
@@ -1510,7 +1511,6 @@
}
bool RefreshRateSelector::isVrrDevice() const {
- std::lock_guard lock(mLock);
return mIsVrrDevice;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 4f491d9..6f9c146 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -383,6 +383,7 @@
Callbacks platform;
Callbacks kernel;
+ Callbacks vrr;
};
void setIdleTimerCallbacks(IdleTimerCallbacks callbacks) EXCLUDES(mIdleTimerCallbacksMutex) {
@@ -501,6 +502,9 @@
std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
REQUIRES(mIdleTimerCallbacksMutex) {
if (!mIdleTimerCallbacks) return {};
+
+ if (mIsVrrDevice) return mIdleTimerCallbacks->vrr;
+
return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel
: mIdleTimerCallbacks->platform;
}
@@ -536,7 +540,7 @@
std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
// Caches whether the device is VRR-compatible based on the active display mode.
- bool mIsVrrDevice GUARDED_BY(mLock) = false;
+ std::atomic_bool mIsVrrDevice = false;
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index c43e942..fbd788b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -942,8 +942,9 @@
{.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
.onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
.kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
- .onExpired =
- [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+ .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }},
+ .vrr = {.onReset = [this] { mSchedulerCallback.vrrDisplayIdle(false); },
+ .onExpired = [this] { mSchedulerCallback.vrrDisplayIdle(true); }}});
pacesetter.selectorPtr->startIdleTimer();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 8dae3ca..900bce0 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -45,7 +45,7 @@
}
void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) {
- if (!ATRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
+ if (!SFTRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
return;
}
diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp
index fba3d58..20c58eb 100644
--- a/services/surfaceflinger/Scheduler/src/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -188,7 +188,7 @@
int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
setDebugState(DebugState::Running);
- if (ATRACE_ENABLED()) {
+ if (SFTRACE_ENABLED()) {
ftl::Concat trace("TimerIteration #", iteration++);
SFTRACE_NAME(trace.c_str());
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d00e762..605770d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3320,9 +3320,9 @@
});
}
- // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
+ // Even though SFTRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
// side-effect of getTotalSize(), so we check that again here
- if (ATRACE_ENABLED()) {
+ if (SFTRACE_ENABLED()) {
// getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
SFTRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
}
@@ -4259,6 +4259,11 @@
getHwComposer().setVsyncEnabled(displayId, enable ? hal::Vsync::ENABLE : hal::Vsync::DISABLE);
}
+// This callback originates from Scheduler::applyPolicy, whose thread context may be the main thread
+// (via Scheduler::chooseRefreshRateForContent) or a OneShotTimer thread. The latter case imposes a
+// deadlock prevention rule: If the main thread is processing hotplug, then mStateLock is locked as
+// the main thread stops the OneShotTimer and joins with its thread. Hence, the OneShotTimer thread
+// must not lock mStateLock in this callback, which would deadlock with the join.
void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest> modeRequests) {
if (mBootStage != BootStage::FINISHED) {
ALOGV("Currently in the boot stage, skipping display mode changes");
@@ -4267,21 +4272,14 @@
SFTRACE_CALL();
- // If this is called from the main thread mStateLock must be locked before
- // Currently the only way to call this function from the main thread is from
- // Scheduler::chooseRefreshRateForContent
-
- ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
for (auto& request : modeRequests) {
const auto& modePtr = request.mode.modePtr;
-
const auto displayId = modePtr->getPhysicalDisplayId();
- const auto display = getDisplayDeviceLocked(displayId);
- if (!display) continue;
+ const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
+ if (!selectorPtr) continue;
- if (display->refreshRateSelector().isModeAllowed(request.mode)) {
+ if (selectorPtr->isModeAllowed(request.mode)) {
setDesiredMode(std::move(request));
} else {
ALOGV("%s: Mode %d is disallowed for display %s", __func__,
@@ -6749,14 +6747,24 @@
rootProto->set_name("Offscreen Root");
rootProto->set_parent(-1);
- for (Layer* offscreenLayer : mOffscreenLayers) {
- // Add layer as child of the fake root
- rootProto->add_children(offscreenLayer->sequence);
+ perfetto::protos::LayersProto offscreenLayers =
+ LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos,
+ mLegacyLayers, traceFlags)
+ .generate(mLayerHierarchyBuilder.getOffscreenHierarchy());
- // Add layer
- auto* layerProto = offscreenLayer->writeToProto(layersProto, traceFlags);
- layerProto->set_parent(offscreenRootLayerId);
+ for (int i = 0; i < offscreenLayers.layers_size(); i++) {
+ perfetto::protos::LayerProto* layerProto = offscreenLayers.mutable_layers()->Mutable(i);
+ if (layerProto->parent() == -1) {
+ layerProto->set_parent(offscreenRootLayerId);
+ // Add layer as child of the fake root
+ rootProto->add_children(layerProto->id());
+ }
}
+
+ layersProto.mutable_layers()->Reserve(layersProto.layers_size() +
+ offscreenLayers.layers_size());
+ std::copy(offscreenLayers.layers().begin(), offscreenLayers.layers().end(),
+ RepeatedFieldBackInserter(layersProto.mutable_layers()));
}
perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
@@ -7693,6 +7701,22 @@
}));
}
+void SurfaceFlinger::vrrDisplayIdle(bool idle) {
+ // Update the overlay on the main thread to avoid race conditions with
+ // RefreshRateSelector::getActiveMode
+ static_cast<void>(mScheduler->schedule([=, this] {
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
+ if (!display) {
+ ALOGW("%s: default display is null", __func__);
+ return;
+ }
+ if (!display->isRefreshRateOverlayEnabled()) return;
+
+ display->onVrrIdle(idle);
+ mScheduler->scheduleFrame();
+ }));
+}
+
std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) {
const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported(
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8242844..2787c1f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -704,6 +704,7 @@
Fps renderRate) override;
void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) override
REQUIRES(kMainThreadContext);
+ void vrrDisplayIdle(bool idle) override;
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
@@ -733,7 +734,7 @@
// Show hdr sdr ratio overlay
bool mHdrSdrRatioOverlay = false;
- void setDesiredMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+ void setDesiredMode(display::DisplayModeRequest&&);
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
Fps maxFps);
@@ -1183,7 +1184,8 @@
perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const
REQUIRES(kMainThreadContext);
void dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto,
- uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
+ uint32_t traceFlags = LayerTracing::TRACE_ALL) const
+ REQUIRES(kMainThreadContext);
google::protobuf::RepeatedPtrField<perfetto::protos::DisplayProto> dumpDisplayProto() const;
void doActiveLayersTracingIfNeeded(bool isCompositionComputed, bool visibleRegionDirty,
TimePoint, VsyncId) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index eba1ecf..51f33a6 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -79,7 +79,7 @@
private:
void trace() {
- if (CC_LIKELY(!ATRACE_ENABLED())) {
+ if (CC_LIKELY(!SFTRACE_ENABLED())) {
return;
}
diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h
index c13cdde..344359e 100644
--- a/services/surfaceflinger/common/include/common/trace.h
+++ b/services/surfaceflinger/common/include/common/trace.h
@@ -20,6 +20,10 @@
#include <cutils/trace.h>
#include <stdint.h>
+#ifndef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#endif
+
#define SFTRACE_ENABLED() ATRACE_ENABLED()
#define SFTRACE_BEGIN(name) ATRACE_BEGIN(name)
#define SFTRACE_END() ATRACE_END()
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 25437ca..9be0fc3 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -479,6 +479,16 @@
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
+TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceUnsignaled) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue",
+ /*isBuffer*/ true, sGameMode);
+ surfaceFrame->setActualQueueTime(123);
+ surfaceFrame->setAcquireFenceTime(Fence::SIGNAL_TIME_PENDING);
+ EXPECT_EQ(surfaceFrame->getActuals().endTime, 123);
+}
+
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
"acquireFenceAfterQueue",
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index dec5fa5..8f21cdb 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -31,6 +31,7 @@
MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
(override));
MOCK_METHOD(void, onCommitNotComposited, (PhysicalDisplayId), (override));
+ MOCK_METHOD(void, vrrDisplayIdle, (bool), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -41,6 +42,7 @@
void onChoreographerAttached() override {}
void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
void onCommitNotComposited(PhysicalDisplayId) override {}
+ void vrrDisplayIdle(bool) override {}
};
} // namespace android::scheduler::mock