Merge "Update expected pre dump file names" into udc-dev
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index e45a656..aca5009 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -75,6 +75,20 @@
return DEAD_OBJECT;
}
+status_t ActivityManager::registerUidObserverForUids(const sp<IUidObserver>& observer,
+ const int32_t event, const int32_t cutpoint,
+ const String16& callingPackage,
+ const int32_t uids[], size_t nUids,
+ /*out*/ sp<IBinder>& observerToken) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->registerUidObserverForUids(observer, event, cutpoint, callingPackage, uids,
+ nUids, observerToken);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
status_t ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer)
{
sp<IActivityManager> service = getService();
@@ -85,6 +99,26 @@
return DEAD_OBJECT;
}
+status_t ActivityManager::addUidToObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->addUidToObserver(observerToken, callingPackage, uid);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
+status_t ActivityManager::removeUidFromObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->removeUidFromObserver(observerToken, callingPackage, uid);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage)
{
sp<IActivityManager> service = getService();
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index ebdaa4c..84900a7 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -77,6 +77,30 @@
return OK;
}
+ virtual status_t registerUidObserverForUids(const sp<IUidObserver>& observer,
+ const int32_t event, const int32_t cutpoint,
+ const String16& callingPackage,
+ const int32_t uids[], size_t nUids,
+ /*out*/ sp<IBinder>& observerToken) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(observer));
+ data.writeInt32(event);
+ data.writeInt32(cutpoint);
+ data.writeString16(callingPackage);
+ data.writeInt32Array(nUids, uids);
+ status_t err =
+ remote()->transact(REGISTER_UID_OBSERVER_FOR_UIDS_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ err = reply.readStrongBinder(&observerToken);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
+ }
+
virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer)
{
Parcel data, reply;
@@ -89,6 +113,34 @@
return OK;
}
+ virtual status_t addUidToObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeStrongBinder(observerToken);
+ data.writeString16(callingPackage);
+ data.writeInt32(uid);
+ status_t err = remote()->transact(ADD_UID_TO_OBSERVER_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
+ }
+
+ virtual status_t removeUidFromObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeStrongBinder(observerToken);
+ data.writeString16(callingPackage);
+ data.writeInt32(uid);
+ status_t err = remote()->transact(REMOVE_UID_FROM_OBSERVER_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
+ }
+
virtual bool isUidActive(const uid_t uid, const String16& callingPackage)
{
Parcel data, reply;
diff --git a/libs/binder/include_activitymanager/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
index 5dfbd44..9c634c7 100644
--- a/libs/binder/include_activitymanager/binder/ActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/ActivityManager.h
@@ -82,7 +82,15 @@
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage);
+ status_t registerUidObserverForUids(const sp<IUidObserver>& observer, const int32_t event,
+ const int32_t cutpoint, const String16& callingPackage,
+ const int32_t uids[], size_t nUids,
+ /*out*/ sp<IBinder>& observerToken);
status_t unregisterUidObserver(const sp<IUidObserver>& observer);
+ status_t addUidToObserver(const sp<IBinder>& observerToken, const String16& callingPackage,
+ int32_t uid);
+ status_t removeUidFromObserver(const sp<IBinder>& observerToken, const String16& callingPackage,
+ int32_t uid);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult);
diff --git a/libs/binder/include_activitymanager/binder/IActivityManager.h b/libs/binder/include_activitymanager/binder/IActivityManager.h
index 20d12ae..07450c6 100644
--- a/libs/binder/include_activitymanager/binder/IActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/IActivityManager.h
@@ -35,7 +35,16 @@
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage) = 0;
+ virtual status_t registerUidObserverForUids(const sp<IUidObserver>& observer,
+ const int32_t event, const int32_t cutpoint,
+ const String16& callingPackage,
+ const int32_t uids[], size_t nUids,
+ /*out*/ sp<IBinder>& observerToken) = 0;
virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
+ virtual status_t addUidToObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) = 0;
+ virtual status_t removeUidFromObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
virtual status_t checkPermission(const String16& permission,
@@ -51,6 +60,9 @@
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
+ REGISTER_UID_OBSERVER_FOR_UIDS_TRANSACTION,
+ ADD_UID_TO_OBSERVER_TRANSACTION,
+ REMOVE_UID_FROM_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
GET_UID_PROCESS_STATE_TRANSACTION,
CHECK_PERMISSION_TRANSACTION,
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index c480056..5fbae3c 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -433,61 +433,24 @@
return (mUseAngle == YES) ? true : false;
}
-bool GraphicsEnv::angleIsSystemDriver() {
- // Make sure we are init'ed
- if (mAngleAppName.empty()) {
- ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
- return false;
- }
-
- return (mAngleIsSystemDriver == YES) ? true : false;
-}
-
-bool GraphicsEnv::shouldForceLegacyDriver() {
- // Make sure we are init'ed
- if (mAngleAppName.empty()) {
- ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
- return false;
- }
-
- return (mAngleIsSystemDriver == YES && mUseAngle == NO) ? true : false;
-}
-
-std::string GraphicsEnv::getLegacySuffix() {
- return mLegacyDriverSuffix;
-}
-
void GraphicsEnv::updateUseAngle() {
- mUseAngle = NO;
-
const char* ANGLE_PREFER_ANGLE = "angle";
- const char* ANGLE_PREFER_LEGACY = "legacy";
- // The following is a deprecated version of "legacy"
const char* ANGLE_PREFER_NATIVE = "native";
mUseAngle = NO;
if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
- ALOGI("Using ANGLE, the %s GLES driver for package '%s'",
- mAngleIsSystemDriver == YES ? "system" : "optional", mAngleAppName.c_str());
+ ALOGV("User set \"Developer Options\" to force the use of ANGLE");
mUseAngle = YES;
- } else if (mAngleDeveloperOptIn == ANGLE_PREFER_LEGACY ||
- mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
- ALOGI("Using the (%s) Legacy GLES driver for package '%s'",
- mAngleIsSystemDriver == YES ? "optional" : "system", mAngleAppName.c_str());
+ } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
+ ALOGV("User set \"Developer Options\" to force the use of Native");
} else {
ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());
}
}
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
- const bool angleIsSystemDriver, const std::string developerOptIn,
+ const std::string developerOptIn,
const std::vector<std::string> eglFeatures) {
- // Set whether ANGLE is the system driver:
- mAngleIsSystemDriver = angleIsSystemDriver ? YES : NO;
-
- // Note: Given the current logic and lack of the old rules file processing,
- // there seems to be little chance that mUseAngle != UNKNOWN. Leave this
- // for now, even though it seems outdated.
if (mUseAngle != UNKNOWN) {
// We've already figured out an answer for this app, so just return.
ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
@@ -508,25 +471,6 @@
updateUseAngle();
}
-void GraphicsEnv::setLegacyDriverInfo(const std::string appName, const bool angleIsSystemDriver,
- const std::string legacyDriverName) {
- ALOGV("setting legacy app name to '%s'", appName.c_str());
- mAngleAppName = appName;
-
- // Force the use of the legacy driver instead of ANGLE
- const char* ANGLE_PREFER_LEGACY = "legacy";
- mAngleDeveloperOptIn = ANGLE_PREFER_LEGACY;
- ALOGV("setting ANGLE application opt-in to 'legacy'");
-
- // Set whether ANGLE is the system driver:
- mAngleIsSystemDriver = angleIsSystemDriver ? YES : NO;
-
- mLegacyDriverSuffix = legacyDriverName;
-
- // Update the current status of whether we should use ANGLE or not
- updateUseAngle();
-}
-
void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) {
if (mLayerPaths.empty()) {
mLayerPaths = layerPaths;
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 1274c46..f9b234a 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -100,28 +100,17 @@
bool shouldUseAngle(std::string appName);
// Check if this app process should use ANGLE.
bool shouldUseAngle();
- // If ANGLE is the system GLES driver
- bool angleIsSystemDriver();
- // If should use legacy driver instead of a system ANGLE driver
- bool shouldForceLegacyDriver();
// Set a search path for loading ANGLE libraries. The path is a list of
// directories separated by ':'. A directory can be contained in a zip file
// (libraries must be stored uncompressed and page aligned); such elements
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
- void setAngleInfo(const std::string path, const std::string appName,
- const bool angleIsSystemDriver, std::string devOptIn,
+ void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
const std::vector<std::string> eglFeatures);
- // Set the state so that the legacy driver will be used, and in case ANGLE
- // is the system driver, provide the name of the legacy driver.
- void setLegacyDriverInfo(const std::string appName, const bool angleIsSystemDriver,
- const std::string legacyDriverName);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app name for ANGLE debug message.
std::string& getAngleAppName();
- // Get the legacy driver's suffix name.
- std::string getLegacySuffix();
const std::vector<std::string>& getAngleEglFeatures();
@@ -178,10 +167,6 @@
std::string mAngleDeveloperOptIn;
// ANGLE EGL features;
std::vector<std::string> mAngleEglFeatures;
- // ANGLE is System Driver flag.
- UseAngle mAngleIsSystemDriver = UNKNOWN;
- // Legacy driver name to use when ANGLE is the system driver.
- std::string mLegacyDriverSuffix;
// Use ANGLE flag.
UseAngle mUseAngle = UNKNOWN;
// Vulkan debug layers libs.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 0a63c15..33bb343 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -75,6 +75,7 @@
"android/gui/IWindowInfosListener.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
"android/gui/WindowInfo.aidl",
+ "android/gui/WindowInfosUpdate.aidl",
],
}
@@ -90,9 +91,11 @@
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
+ "android/gui/WindowInfosUpdate.aidl",
"android/gui/WindowInfo.aidl",
"DisplayInfo.cpp",
"WindowInfo.cpp",
+ "WindowInfosUpdate.cpp",
],
shared_libs: [
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 2b34a0f..76e7b6e 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -17,6 +17,7 @@
#include <android/gui/ISurfaceComposer.h>
#include <gui/AidlStatusUtil.h>
#include <gui/WindowInfosListenerReporter.h>
+#include "gui/WindowInfosUpdate.h"
namespace android {
@@ -84,7 +85,7 @@
}
binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
- const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
+ const gui::WindowInfosUpdate& update,
const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
std::unordered_set<sp<WindowInfosListener>, gui::SpHash<WindowInfosListener>>
windowInfosListeners;
@@ -95,12 +96,12 @@
windowInfosListeners.insert(listener);
}
- mLastWindowInfos = windowInfos;
- mLastDisplayInfos = displayInfos;
+ mLastWindowInfos = update.windowInfos;
+ mLastDisplayInfos = update.displayInfos;
}
for (auto listener : windowInfosListeners) {
- listener->onWindowInfosChanged(windowInfos, displayInfos);
+ listener->onWindowInfosChanged(update);
}
if (windowInfosReportedListener) {
diff --git a/libs/gui/WindowInfosUpdate.cpp b/libs/gui/WindowInfosUpdate.cpp
new file mode 100644
index 0000000..38ae5ef
--- /dev/null
+++ b/libs/gui/WindowInfosUpdate.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/WindowInfosUpdate.h>
+#include <private/gui/ParcelUtils.h>
+
+namespace android::gui {
+
+status_t WindowInfosUpdate::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ uint32_t size;
+
+ SAFE_PARCEL(parcel->readUint32, &size);
+ windowInfos.reserve(size);
+ for (uint32_t i = 0; i < size; i++) {
+ windowInfos.push_back({});
+ SAFE_PARCEL(windowInfos.back().readFromParcel, parcel);
+ }
+
+ SAFE_PARCEL(parcel->readUint32, &size);
+ displayInfos.reserve(size);
+ for (uint32_t i = 0; i < size; i++) {
+ displayInfos.push_back({});
+ SAFE_PARCEL(displayInfos.back().readFromParcel, parcel);
+ }
+
+ SAFE_PARCEL(parcel->readInt64, &vsyncId);
+ SAFE_PARCEL(parcel->readInt64, ×tamp);
+
+ return OK;
+}
+
+status_t WindowInfosUpdate::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(windowInfos.size()));
+ for (auto& windowInfo : windowInfos) {
+ SAFE_PARCEL(windowInfo.writeToParcel, parcel);
+ }
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(displayInfos.size()));
+ for (auto& displayInfo : displayInfos) {
+ SAFE_PARCEL(displayInfo.writeToParcel, parcel);
+ }
+
+ SAFE_PARCEL(parcel->writeInt64, vsyncId);
+ SAFE_PARCEL(parcel->writeInt64, timestamp);
+
+ return OK;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
index a5b2762..400229d 100644
--- a/libs/gui/android/gui/IWindowInfosListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -16,12 +16,11 @@
package android.gui;
-import android.gui.DisplayInfo;
import android.gui.IWindowInfosReportedListener;
-import android.gui.WindowInfo;
+import android.gui.WindowInfosUpdate;
/** @hide */
-oneway interface IWindowInfosListener
-{
- void onWindowInfosChanged(in WindowInfo[] windowInfos, in DisplayInfo[] displayInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+oneway interface IWindowInfosListener {
+ void onWindowInfosChanged(
+ in WindowInfosUpdate update, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
}
diff --git a/libs/gui/android/gui/WindowInfosUpdate.aidl b/libs/gui/android/gui/WindowInfosUpdate.aidl
new file mode 100644
index 0000000..0c6109d
--- /dev/null
+++ b/libs/gui/android/gui/WindowInfosUpdate.aidl
@@ -0,0 +1,22 @@
+/*
+** Copyright 2023, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.gui;
+
+import android.gui.DisplayInfo;
+import android.gui.WindowInfo;
+
+parcelable WindowInfosUpdate cpp_header "gui/WindowInfosUpdate.h";
diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h
index a18a498..02c8eb5 100644
--- a/libs/gui/include/gui/WindowInfosListener.h
+++ b/libs/gui/include/gui/WindowInfosListener.h
@@ -16,15 +16,13 @@
#pragma once
-#include <gui/DisplayInfo.h>
-#include <gui/WindowInfo.h>
+#include <gui/WindowInfosUpdate.h>
#include <utils/RefBase.h>
namespace android::gui {
class WindowInfosListener : public virtual RefBase {
public:
- virtual void onWindowInfosChanged(const std::vector<WindowInfo>&,
- const std::vector<DisplayInfo>&) = 0;
+ virtual void onWindowInfosChanged(const WindowInfosUpdate& update) = 0;
};
-} // namespace android::gui
\ No newline at end of file
+} // namespace android::gui
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 2754442..38cb108 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -22,6 +22,7 @@
#include <binder/IBinder.h>
#include <gui/SpHash.h>
#include <gui/WindowInfosListener.h>
+#include <gui/WindowInfosUpdate.h>
#include <unordered_set>
namespace android {
@@ -29,8 +30,7 @@
class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
public:
static sp<WindowInfosListenerReporter> getInstance();
- binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>&,
- const std::vector<gui::DisplayInfo>&,
+ binder::Status onWindowInfosChanged(const gui::WindowInfosUpdate& update,
const sp<gui::IWindowInfosReportedListener>&) override;
status_t addWindowInfosListener(
const sp<gui::WindowInfosListener>& windowInfosListener,
diff --git a/libs/gui/include/gui/WindowInfosUpdate.h b/libs/gui/include/gui/WindowInfosUpdate.h
new file mode 100644
index 0000000..2ca59fb
--- /dev/null
+++ b/libs/gui/include/gui/WindowInfosUpdate.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcelable.h>
+#include <gui/DisplayInfo.h>
+#include <gui/WindowInfo.h>
+
+namespace android::gui {
+
+struct WindowInfosUpdate : public Parcelable {
+ WindowInfosUpdate() {}
+
+ WindowInfosUpdate(std::vector<WindowInfo> windowInfos, std::vector<DisplayInfo> displayInfos,
+ int64_t vsyncId, int64_t timestamp)
+ : windowInfos(std::move(windowInfos)),
+ displayInfos(std::move(displayInfos)),
+ vsyncId(vsyncId),
+ timestamp(timestamp) {}
+
+ std::vector<WindowInfo> windowInfos;
+ std::vector<DisplayInfo> displayInfos;
+ int64_t vsyncId;
+ int64_t timestamp;
+
+ status_t writeToParcel(android::Parcel*) const override;
+ status_t readFromParcel(const android::Parcel*) override;
+};
+
+} // namespace android::gui
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 415e8ea..2c3ce16 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -139,9 +139,10 @@
static const char* DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
-static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
- DRIVER_SUFFIX_PROPERTY,
- "ro.board.platform",
+static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = {
+ "persist.graphics.egl",
+ DRIVER_SUFFIX_PROPERTY,
+ "ro.board.platform",
};
static bool should_unload_system_driver(egl_connection_t* cnx) {
@@ -208,8 +209,7 @@
ATRACE_CALL();
const nsecs_t openTime = systemTime();
- if (!android::GraphicsEnv::getInstance().angleIsSystemDriver() &&
- should_unload_system_driver(cnx)) {
+ if (should_unload_system_driver(cnx)) {
unload_system_driver(cnx);
}
@@ -218,12 +218,8 @@
return cnx->dso;
}
- // Firstly, try to load ANGLE driver, unless we know that we shouldn't.
- bool shouldForceLegacyDriver = android::GraphicsEnv::getInstance().shouldForceLegacyDriver();
- driver_t* hnd = nullptr;
- if (!shouldForceLegacyDriver) {
- hnd = attempt_to_load_angle(cnx);
- }
+ // Firstly, try to load ANGLE driver.
+ driver_t* hnd = attempt_to_load_angle(cnx);
if (!hnd) {
// Secondly, try to load from driver apk.
@@ -285,8 +281,10 @@
}
LOG_ALWAYS_FATAL_IF(!hnd,
- "couldn't find an OpenGL ES implementation, make sure you set %s or %s",
- HAL_SUBNAME_KEY_PROPERTIES[0], HAL_SUBNAME_KEY_PROPERTIES[1]);
+ "couldn't find an OpenGL ES implementation, make sure one of %s, %s and %s "
+ "is set",
+ HAL_SUBNAME_KEY_PROPERTIES[0], HAL_SUBNAME_KEY_PROPERTIES[1],
+ HAL_SUBNAME_KEY_PROPERTIES[2]);
if (!cnx->libEgl) {
cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 9823fc8..525fed1 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -326,10 +326,10 @@
// device's present timestamps are reliable (which may not be the case on emulators).
if (cnx->useAngle) {
if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
- mExtensionString.append("EGL_ANDROID_get_frame_timestamps");
+ mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
}
} else {
- mExtensionString.append("EGL_ANDROID_get_frame_timestamps");
+ mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
}
hasColorSpaceSupport = findExtension(disp.queryString.extensions, "EGL_KHR_gl_colorspace");
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index cf299c0..f65533e 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -340,8 +340,10 @@
std::vector<gui::DisplayInfo> displayInfos{info};
for (auto _ : state) {
- dispatcher.onWindowInfosChanged(windowInfos, displayInfos);
- dispatcher.onWindowInfosChanged(/*windowInfos=*/{}, /*displayInfos=*/{});
+ dispatcher.onWindowInfosChanged(
+ {windowInfos, displayInfos, /*vsyncId=*/0, /*timestamp=*/0});
+ dispatcher.onWindowInfosChanged(
+ {/*windowInfos=*/{}, /*displayInfos=*/{}, /*vsyncId=*/{}, /*timestamp=*/0});
}
dispatcher.stop();
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index e4f367d..326ca87 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2239,7 +2239,9 @@
const bool wasDown = oldState != nullptr && oldState->isDown();
const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
(maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
- const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction;
+ const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
// If pointers are already down, let's finish the current gesture and ignore the new events
@@ -2427,7 +2429,7 @@
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (!tempTouchState.isDown()) {
+ if (!tempTouchState.isDown() && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
LOG(INFO) << "Dropping event because the pointer is not down or we previously "
"dropped the pointer down event in display "
<< displayId << ": " << entry.getDescription();
@@ -2435,6 +2437,20 @@
return {};
}
+ // If the pointer is not currently hovering, then ignore the event.
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ const int32_t pointerId = entry.pointerProperties[0].id;
+ if (oldState == nullptr ||
+ oldState->getWindowsWithHoveringPointer(entry.deviceId, pointerId).empty()) {
+ LOG(INFO) << "Dropping event because the hovering pointer is not in any windows in "
+ "display "
+ << displayId << ": " << entry.getDescription();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
+ }
+ tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
+ }
+
addDragEventLocked(entry);
// Check whether touches should slip outside of the current foreground window.
@@ -2530,21 +2546,6 @@
targets);
}
}
- // Ensure that we have at least one foreground window or at least one window that cannot be a
- // foreground target. If we only have windows that are not receiving foreground touches (e.g. we
- // only have windows getting ACTION_OUTSIDE), then drop the event, because there is no window
- // that is actually receiving the entire gesture.
- if (std::none_of(tempTouchState.windows.begin(), tempTouchState.windows.end(),
- [](const TouchedWindow& touchedWindow) {
- return !canReceiveForegroundTouches(
- *touchedWindow.windowHandle->getInfo()) ||
- touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND);
- })) {
- ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
- displayId, entry.getDescription().c_str());
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
- }
// Ensure that all touched windows are valid for injection.
if (entry.injectionState != nullptr) {
@@ -2586,7 +2587,7 @@
}
}
- // Success! Output targets from the touch state.
+ // Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
// Windows with hovering pointers are getting persisted inside TouchState.
@@ -2598,6 +2599,23 @@
targets);
}
+ if (targets.empty()) {
+ LOG(INFO) << "Dropping event because no targets were found: " << entry.getDescription();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
+ }
+
+ // If we only have windows getting ACTION_OUTSIDE, then drop the event, because there is no
+ // window that is actually receiving the entire gesture.
+ if (std::all_of(targets.begin(), targets.end(), [](const InputTarget& target) {
+ return target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE);
+ })) {
+ LOG(INFO) << "Dropping event because all windows would just receive ACTION_OUTSIDE: "
+ << entry.getDescription();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
+ }
+
outInjectionResult = InputEventInjectionResult::SUCCEEDED;
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
@@ -5483,7 +5501,7 @@
std::string line;
while (std::getline(stream, line, '\n')) {
- ALOGD("%s", line.c_str());
+ ALOGI("%s", line.c_str());
}
}
@@ -5593,6 +5611,14 @@
} else {
dump += INDENT "Displays: <none>\n";
}
+ dump += INDENT "Window Infos:\n";
+ dump += StringPrintf(INDENT2 "vsync id: %" PRId64 "\n", mWindowInfosVsyncId);
+ dump += StringPrintf(INDENT2 "timestamp (ns): %" PRId64 "\n", mWindowInfosTimestamp);
+ dump += "\n";
+ dump += StringPrintf(INDENT2 "max update delay (ns): %" PRId64 "\n", mMaxWindowInfosDelay);
+ dump += StringPrintf(INDENT2 "max update delay vsync id: %" PRId64 "\n",
+ mMaxWindowInfosDelayVsyncId);
+ dump += "\n";
if (!mGlobalMonitorsByDisplay.empty()) {
for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) {
@@ -6608,12 +6634,11 @@
mLooper->wake();
}
-void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
- const std::vector<DisplayInfo>& displayInfos) {
+void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) {
// The listener sends the windows as a flattened array. Separate the windows by display for
// more convenient parsing.
std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
- for (const auto& info : windowInfos) {
+ for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
}
@@ -6628,13 +6653,22 @@
}
mDisplayInfos.clear();
- for (const auto& displayInfo : displayInfos) {
+ for (const auto& displayInfo : update.displayInfos) {
mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
}
for (const auto& [displayId, handles] : handlesPerDisplay) {
setInputWindowsLocked(handles, displayId);
}
+
+ mWindowInfosVsyncId = update.vsyncId;
+ mWindowInfosTimestamp = update.timestamp;
+
+ int64_t delay = systemTime() - update.timestamp;
+ if (delay > mMaxWindowInfosDelay) {
+ mMaxWindowInfosDelay = delay;
+ mMaxWindowInfosDelayVsyncId = update.vsyncId;
+ }
}
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
@@ -6657,9 +6691,8 @@
}
void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
- const std::vector<gui::WindowInfo>& windowInfos,
- const std::vector<DisplayInfo>& displayInfos) {
- mDispatcher.onWindowInfosChanged(windowInfos, displayInfos);
+ const gui::WindowInfosUpdate& update) {
+ mDispatcher.onWindowInfosChanged(update);
}
void InputDispatcher::cancelCurrentTouch() {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 0e9ccfb..9b12f2f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -36,7 +36,7 @@
#include <attestation/HmacKeyManager.h>
#include <gui/InputApplication.h>
-#include <gui/WindowInfo.h>
+#include <gui/WindowInfosUpdate.h>
#include <input/Input.h>
#include <input/InputTransport.h>
#include <limits.h>
@@ -144,8 +144,7 @@
void displayRemoved(int32_t displayId) override;
// Public because it's also used by tests to simulate the WindowInfosListener callback
- void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos,
- const std::vector<android::gui::DisplayInfo>& displayInfos);
+ void onWindowInfosChanged(const gui::WindowInfosUpdate&);
void cancelCurrentTouch() override;
@@ -205,6 +204,11 @@
const IdGenerator mIdGenerator;
+ int64_t mWindowInfosVsyncId GUARDED_BY(mLock);
+ int64_t mWindowInfosTimestamp GUARDED_BY(mLock);
+ int64_t mMaxWindowInfosDelay GUARDED_BY(mLock) = -1;
+ int64_t mMaxWindowInfosDelayVsyncId GUARDED_BY(mLock) = -1;
+
// With each iteration, InputDispatcher nominally processes one queued event,
// a timeout, or a response from an input consumer.
// This method should only be called on the input dispatcher's own thread.
@@ -356,9 +360,7 @@
class DispatcherWindowListener : public gui::WindowInfosListener {
public:
explicit DispatcherWindowListener(InputDispatcher& dispatcher) : mDispatcher(dispatcher){};
- void onWindowInfosChanged(
- const std::vector<android::gui::WindowInfo>& windowInfos,
- const std::vector<android::gui::DisplayInfo>& displayInfos) override;
+ void onWindowInfosChanged(const gui::WindowInfosUpdate&) override;
private:
InputDispatcher& mDispatcher;
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 132c3a1..b0edb57 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -42,6 +42,7 @@
"Macros.cpp",
"TouchVideoDevice.cpp",
"controller/PeripheralController.cpp",
+ "mapper/CapturedTouchpadEventConverter.cpp",
"mapper/CursorInputMapper.cpp",
"mapper/ExternalStylusInputMapper.cpp",
"mapper/InputMapper.cpp",
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
new file mode 100644
index 0000000..dab4661
--- /dev/null
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CapturedTouchpadEventConverter.h"
+
+#include <sstream>
+
+#include <android-base/stringprintf.h>
+#include <gui/constants.h>
+#include <input/PrintTools.h>
+#include <linux/input-event-codes.h>
+#include <log/log_main.h>
+
+namespace android {
+
+namespace {
+
+int32_t actionWithIndex(int32_t action, int32_t index) {
+ return action | (index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+}
+
+template <typename T>
+size_t firstUnmarkedBit(T set) {
+ // TODO: replace with std::countr_one from <bit> when that's available
+ LOG_ALWAYS_FATAL_IF(set.all());
+ size_t i = 0;
+ while (set.test(i)) {
+ i++;
+ }
+ return i;
+}
+
+} // namespace
+
+CapturedTouchpadEventConverter::CapturedTouchpadEventConverter(
+ InputReaderContext& readerContext, const InputDeviceContext& deviceContext,
+ MultiTouchMotionAccumulator& motionAccumulator, int32_t deviceId)
+ : mDeviceId(deviceId),
+ mReaderContext(readerContext),
+ mDeviceContext(deviceContext),
+ mMotionAccumulator(motionAccumulator),
+ mHasTouchMinor(deviceContext.hasAbsoluteAxis(ABS_MT_TOUCH_MINOR)),
+ mHasToolMinor(deviceContext.hasAbsoluteAxis(ABS_MT_WIDTH_MINOR)) {
+ RawAbsoluteAxisInfo orientationInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
+ if (orientationInfo.valid) {
+ if (orientationInfo.maxValue > 0) {
+ mOrientationScale = M_PI_2 / orientationInfo.maxValue;
+ } else if (orientationInfo.minValue < 0) {
+ mOrientationScale = -M_PI_2 / orientationInfo.minValue;
+ }
+ }
+
+ // TODO(b/275369880): support touch.pressure.calibration and .scale properties when captured.
+ RawAbsoluteAxisInfo pressureInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
+ if (pressureInfo.valid && pressureInfo.maxValue > 0) {
+ mPressureScale = 1.0 / pressureInfo.maxValue;
+ }
+
+ RawAbsoluteAxisInfo touchMajorInfo, toolMajorInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &touchMajorInfo);
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &toolMajorInfo);
+ mHasTouchMajor = touchMajorInfo.valid;
+ mHasToolMajor = toolMajorInfo.valid;
+ if (mHasTouchMajor && touchMajorInfo.maxValue != 0) {
+ mSizeScale = 1.0f / touchMajorInfo.maxValue;
+ } else if (mHasToolMajor && toolMajorInfo.maxValue != 0) {
+ mSizeScale = 1.0f / toolMajorInfo.maxValue;
+ }
+}
+
+std::string CapturedTouchpadEventConverter::dump() const {
+ std::stringstream out;
+ out << "Orientation scale: " << mOrientationScale << "\n";
+ out << "Pressure scale: " << mPressureScale << "\n";
+ out << "Size scale: " << mSizeScale << "\n";
+
+ out << "Dimension axes:";
+ if (mHasTouchMajor) out << " touch major";
+ if (mHasTouchMinor) out << ", touch minor";
+ if (mHasToolMajor) out << ", tool major";
+ if (mHasToolMinor) out << ", tool minor";
+ out << "\n";
+
+ out << "Down time: " << mDownTime << "\n";
+ out << StringPrintf("Button state: 0x%08x\n", mButtonState);
+
+ out << StringPrintf("Pointer IDs in use: %s\n", mPointerIdsInUse.to_string().c_str());
+
+ out << "Pointer IDs for slot numbers:\n";
+ out << addLinePrefix(dumpMap(mPointerIdForSlotNumber), " ") << "\n";
+ return out.str();
+}
+
+void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) const {
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MINOR, ABS_MT_TOUCH_MINOR);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MINOR, ABS_MT_WIDTH_MINOR);
+
+ RawAbsoluteAxisInfo pressureInfo;
+ mDeviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
+ if (pressureInfo.valid) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0, 1, 0, 0, 0);
+ }
+
+ RawAbsoluteAxisInfo orientationInfo;
+ mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
+ if (orientationInfo.valid && (orientationInfo.maxValue > 0 || orientationInfo.minValue < 0)) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, SOURCE, -M_PI_2, M_PI_2, 0, 0, 0);
+ }
+
+ if (mHasTouchMajor || mHasToolMajor) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_SIZE, SOURCE, 0, 1, 0, 0, 0);
+ }
+}
+
+void CapturedTouchpadEventConverter::tryAddRawMotionRange(InputDeviceInfo& deviceInfo,
+ int32_t androidAxis,
+ int32_t evdevAxis) const {
+ RawAbsoluteAxisInfo info;
+ mDeviceContext.getAbsoluteAxisInfo(evdevAxis, &info);
+ if (info.valid) {
+ deviceInfo.addMotionRange(androidAxis, SOURCE, info.minValue, info.maxValue, info.flat,
+ info.fuzz, info.resolution);
+ }
+}
+
+void CapturedTouchpadEventConverter::reset() {
+ mCursorButtonAccumulator.reset(mDeviceContext);
+ mDownTime = 0;
+ mPointerIdsInUse.reset();
+ mPointerIdForSlotNumber.clear();
+}
+
+std::list<NotifyArgs> CapturedTouchpadEventConverter::process(const RawEvent& rawEvent) {
+ std::list<NotifyArgs> out;
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+ out = sync(rawEvent.when, rawEvent.readTime);
+ mMotionAccumulator.finishSync();
+ }
+
+ mCursorButtonAccumulator.process(&rawEvent);
+ mMotionAccumulator.process(&rawEvent);
+ return out;
+}
+
+std::list<NotifyArgs> CapturedTouchpadEventConverter::sync(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ std::vector<PointerCoords> coords;
+ std::vector<PointerProperties> properties;
+ std::map<size_t, size_t> coordsIndexForSlotNumber;
+
+ // For all the touches that were already down, send a MOVE event with their updated coordinates.
+ // A convention of the MotionEvent API is that pointer coordinates in UP events match the
+ // pointer's coordinates from the previous MOVE, so we still include touches here even if
+ // they've been lifted in this evdev frame.
+ if (!mPointerIdForSlotNumber.empty()) {
+ for (const auto [slotNumber, pointerId] : mPointerIdForSlotNumber) {
+ // Note that we don't check whether the touch has actually moved — it's rare for a touch
+ // to stay perfectly still between frames, and if it does the worst that can happen is
+ // an extra MOVE event, so it's not worth the overhead of checking for changes.
+ coordsIndexForSlotNumber[slotNumber] = coords.size();
+ coords.push_back(makePointerCoordsForSlot(mMotionAccumulator.getSlot(slotNumber)));
+ properties.push_back({.id = pointerId, .toolType = ToolType::FINGER});
+ }
+ out.push_back(
+ makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, coords, properties));
+ }
+
+ std::vector<size_t> upSlots, downSlots;
+ for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+ const MultiTouchMotionAccumulator::Slot& slot = mMotionAccumulator.getSlot(i);
+ // Some touchpads continue to report contacts even after they've identified them as palms.
+ // We don't currently have a way to mark these as palms when reporting to apps, so don't
+ // report them at all.
+ const bool isInUse = slot.isInUse() && slot.getToolType() != ToolType::PALM;
+ const bool wasInUse = mPointerIdForSlotNumber.find(i) != mPointerIdForSlotNumber.end();
+ if (isInUse && !wasInUse) {
+ downSlots.push_back(i);
+ } else if (!isInUse && wasInUse) {
+ upSlots.push_back(i);
+ }
+ }
+
+ // For any touches that were lifted, send UP or POINTER_UP events.
+ for (size_t slotNumber : upSlots) {
+ const size_t indexToRemove = coordsIndexForSlotNumber.at(slotNumber);
+ const bool cancel = mMotionAccumulator.getSlot(slotNumber).getToolType() == ToolType::PALM;
+ int32_t action;
+ if (coords.size() == 1) {
+ action = cancel ? AMOTION_EVENT_ACTION_CANCEL : AMOTION_EVENT_ACTION_UP;
+ } else {
+ action = actionWithIndex(AMOTION_EVENT_ACTION_POINTER_UP, indexToRemove);
+ }
+ out.push_back(makeMotionArgs(when, readTime, action, coords, properties, /*actionButton=*/0,
+ /*flags=*/cancel ? AMOTION_EVENT_FLAG_CANCELED : 0));
+
+ freePointerIdForSlot(slotNumber);
+ coords.erase(coords.begin() + indexToRemove);
+ properties.erase(properties.begin() + indexToRemove);
+ // Now that we've removed some coords and properties, we might have to update the slot
+ // number to coords index mapping.
+ coordsIndexForSlotNumber.erase(slotNumber);
+ for (auto& [_, index] : coordsIndexForSlotNumber) {
+ if (index > indexToRemove) {
+ index--;
+ }
+ }
+ }
+
+ // For new touches, send DOWN or POINTER_DOWN events.
+ for (size_t slotNumber : downSlots) {
+ const size_t coordsIndex = coords.size();
+ const int32_t action = coords.empty()
+ ? AMOTION_EVENT_ACTION_DOWN
+ : actionWithIndex(AMOTION_EVENT_ACTION_POINTER_DOWN, coordsIndex);
+
+ coordsIndexForSlotNumber[slotNumber] = coordsIndex;
+ coords.push_back(makePointerCoordsForSlot(mMotionAccumulator.getSlot(slotNumber)));
+ properties.push_back(
+ {.id = allocatePointerIdToSlot(slotNumber), .toolType = ToolType::FINGER});
+
+ out.push_back(makeMotionArgs(when, readTime, action, coords, properties));
+ }
+
+ const uint32_t newButtonState = mCursorButtonAccumulator.getButtonState();
+ for (uint32_t button = 1; button <= AMOTION_EVENT_BUTTON_FORWARD; button <<= 1) {
+ if (newButtonState & button && !(mButtonState & button)) {
+ mButtonState |= button;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, coords,
+ properties, /*actionButton=*/button));
+ } else if (!(newButtonState & button) && mButtonState & button) {
+ mButtonState &= ~button;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ coords, properties, /*actionButton=*/button));
+ }
+ }
+ return out;
+}
+
+NotifyMotionArgs CapturedTouchpadEventConverter::makeMotionArgs(
+ nsecs_t when, nsecs_t readTime, int32_t action, const std::vector<PointerCoords>& coords,
+ const std::vector<PointerProperties>& properties, int32_t actionButton, int32_t flags) {
+ LOG_ALWAYS_FATAL_IF(coords.size() != properties.size(),
+ "Mismatched coords and properties arrays.");
+ return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, SOURCE,
+ ADISPLAY_ID_NONE, /*policyFlags=*/POLICY_FLAG_WAKE, action,
+ /*actionButton=*/actionButton, flags,
+ mReaderContext.getGlobalMetaState(), mButtonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, coords.size(),
+ properties.data(), coords.data(), /*xPrecision=*/1.0f,
+ /*yPrecision=*/1.0f, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, mDownTime, /*videoFrames=*/{});
+}
+
+PointerCoords CapturedTouchpadEventConverter::makePointerCoordsForSlot(
+ const MultiTouchMotionAccumulator::Slot& slot) const {
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, slot.getX());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, slot.getY());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, slot.getTouchMajor());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, slot.getTouchMinor());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, slot.getToolMajor());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, slot.getToolMinor());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, slot.getOrientation() * mOrientationScale);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, slot.getPressure() * mPressureScale);
+ float size = 0;
+ // TODO(b/275369880): support touch.size.calibration and .isSummed properties when captured.
+ if (mHasTouchMajor) {
+ size = mHasTouchMinor ? (slot.getTouchMajor() + slot.getTouchMinor()) / 2
+ : slot.getTouchMajor();
+ } else if (mHasToolMajor) {
+ size = mHasToolMinor ? (slot.getToolMajor() + slot.getToolMinor()) / 2
+ : slot.getToolMajor();
+ }
+ coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size * mSizeScale);
+ return coords;
+}
+
+int32_t CapturedTouchpadEventConverter::allocatePointerIdToSlot(size_t slotNumber) {
+ const int32_t pointerId = firstUnmarkedBit(mPointerIdsInUse);
+ mPointerIdsInUse.set(pointerId);
+ mPointerIdForSlotNumber[slotNumber] = pointerId;
+ return pointerId;
+}
+
+void CapturedTouchpadEventConverter::freePointerIdForSlot(size_t slotNumber) {
+ mPointerIdsInUse.reset(mPointerIdForSlotNumber.at(slotNumber));
+ mPointerIdForSlotNumber.erase(slotNumber);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h
new file mode 100644
index 0000000..9b6df7a
--- /dev/null
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bitset>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android/input.h>
+#include <input/Input.h>
+#include <utils/Timers.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "accumulator/CursorButtonAccumulator.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
+#include "accumulator/TouchButtonAccumulator.h"
+
+namespace android {
+
+class CapturedTouchpadEventConverter {
+public:
+ explicit CapturedTouchpadEventConverter(InputReaderContext& readerContext,
+ const InputDeviceContext& deviceContext,
+ MultiTouchMotionAccumulator& motionAccumulator,
+ int32_t deviceId);
+ std::string dump() const;
+ void populateMotionRanges(InputDeviceInfo& info) const;
+ void reset();
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent);
+
+private:
+ void tryAddRawMotionRange(InputDeviceInfo& deviceInfo, int32_t androidAxis,
+ int32_t evdevAxis) const;
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+ const std::vector<PointerCoords>& coords,
+ const std::vector<PointerProperties>& properties,
+ int32_t actionButton = 0, int32_t flags = 0);
+ PointerCoords makePointerCoordsForSlot(const MultiTouchMotionAccumulator::Slot& slot) const;
+ int32_t allocatePointerIdToSlot(size_t slotNumber);
+ void freePointerIdForSlot(size_t slotNumber);
+
+ const int32_t mDeviceId;
+ InputReaderContext& mReaderContext;
+ const InputDeviceContext& mDeviceContext;
+ CursorButtonAccumulator mCursorButtonAccumulator;
+ MultiTouchMotionAccumulator& mMotionAccumulator;
+
+ float mOrientationScale = 0;
+ float mPressureScale = 1;
+ float mSizeScale = 0;
+ bool mHasTouchMajor;
+ const bool mHasTouchMinor;
+ bool mHasToolMajor;
+ const bool mHasToolMinor;
+ nsecs_t mDownTime = 0;
+ uint32_t mButtonState = 0;
+
+ std::bitset<MAX_POINTER_ID + 1> mPointerIdsInUse;
+ std::map<size_t, int32_t> mPointerIdForSlotNumber;
+
+ static constexpr uint32_t SOURCE = AINPUT_SOURCE_TOUCHPAD;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 8753b48..c72425a 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -16,9 +16,11 @@
#include "../Macros.h"
+#include <chrono>
#include <limits>
#include <optional>
+#include <android-base/stringprintf.h>
#include <android/input.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
@@ -174,8 +176,18 @@
: InputMapper(deviceContext, readerConfig),
mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
mPointerController(getContext()->getPointerController(getDeviceId())),
- mStateConverter(deviceContext),
- mGestureConverter(*getContext(), deviceContext, getDeviceId()) {
+ mStateConverter(deviceContext, mMotionAccumulator),
+ mGestureConverter(*getContext(), deviceContext, getDeviceId()),
+ mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()) {
+ RawAbsoluteAxisInfo slotAxisInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+ if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
+ ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
+ "properly.",
+ deviceContext.getName().c_str());
+ }
+ mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
+
mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
// Even though we don't explicitly delete copy/move semantics, it's safe to
@@ -209,15 +221,28 @@
void TouchpadInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
- mGestureConverter.populateMotionRanges(info);
+ if (mPointerCaptured) {
+ mCapturedEventConverter.populateMotionRanges(info);
+ } else {
+ mGestureConverter.populateMotionRanges(info);
+ }
}
void TouchpadInputMapper::dump(std::string& dump) {
dump += INDENT2 "Touchpad Input Mapper:\n";
+ if (mProcessing) {
+ dump += INDENT3 "Currently processing a hardware state\n";
+ }
+ if (mResettingInterpreter) {
+ dump += INDENT3 "Currently resetting gesture interpreter\n";
+ }
+ dump += StringPrintf(INDENT3 "Pointer captured: %s\n", toString(mPointerCaptured));
dump += INDENT3 "Gesture converter:\n";
dump += addLinePrefix(mGestureConverter.dump(), INDENT4);
dump += INDENT3 "Gesture properties:\n";
dump += addLinePrefix(mPropertyProvider.dump(), INDENT4);
+ dump += INDENT3 "Captured event converter:\n";
+ dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
}
std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when,
@@ -245,6 +270,14 @@
accelCurveProp.setRealValues(
createAccelerationCurveForSensitivity(config.touchpadPointerSpeed,
accelCurveProp.getCount()));
+ mPropertyProvider.getProperty("Use Custom Touchpad Scroll Accel Curve")
+ .setBoolValues({true});
+ GesturesProp scrollCurveProp = mPropertyProvider.getProperty("Scroll Accel Curve");
+ scrollCurveProp.setRealValues(
+ createAccelerationCurveForSensitivity(config.touchpadPointerSpeed,
+ scrollCurveProp.getCount()));
+ mPropertyProvider.getProperty("Scroll X Out Scale").setRealValues({1.0});
+ mPropertyProvider.getProperty("Scroll Y Out Scale").setRealValues({1.0});
mPropertyProvider.getProperty("Invert Scrolling")
.setBoolValues({config.touchpadNaturalScrollingEnabled});
mPropertyProvider.getProperty("Tap Enable")
@@ -252,17 +285,50 @@
mPropertyProvider.getProperty("Button Right Click Zone Enable")
.setBoolValues({config.touchpadRightClickZoneEnabled});
}
- return {};
+ std::list<NotifyArgs> out;
+ if ((!changes.any() && config.pointerCaptureRequest.enable) ||
+ changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE)) {
+ mPointerCaptured = config.pointerCaptureRequest.enable;
+ // The motion ranges are going to change, so bump the generation to clear the cached ones.
+ bumpGeneration();
+ if (mPointerCaptured) {
+ // The touchpad is being captured, so we need to tidy up any fake fingers etc. that are
+ // still being reported for a gesture in progress.
+ out += reset(when);
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ } else {
+ // We're transitioning from captured to uncaptured.
+ mCapturedEventConverter.reset();
+ }
+ if (changes.any()) {
+ out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
+ }
+ }
+ return out;
}
std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
mStateConverter.reset();
+ resetGestureInterpreter(when);
std::list<NotifyArgs> out = mGestureConverter.reset(when);
out += InputMapper::reset(when);
return out;
}
+void TouchpadInputMapper::resetGestureInterpreter(nsecs_t when) {
+ // The GestureInterpreter has no official reset method, but sending a HardwareState with no
+ // fingers down or buttons pressed should get it into a clean state.
+ HardwareState state;
+ state.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
+ mResettingInterpreter = true;
+ mGestureInterpreter->PushHardwareState(&state);
+ mResettingInterpreter = false;
+}
+
std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+ if (mPointerCaptured) {
+ return mCapturedEventConverter.process(*rawEvent);
+ }
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
@@ -283,6 +349,11 @@
void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
+ if (mResettingInterpreter) {
+ // We already handle tidying up fake fingers etc. in GestureConverter::reset, so we should
+ // ignore any gestures produced from the interpreter while we're resetting it.
+ return;
+ }
if (!mProcessing) {
ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
return;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 268b275..3128d18 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -21,12 +21,15 @@
#include <vector>
#include <PointerControllerInterface.h>
+#include <utils/Timers.h>
+#include "CapturedTouchpadEventConverter.h"
#include "EventHub.h"
#include "InputDevice.h"
#include "InputMapper.h"
#include "InputReaderBase.h"
#include "NotifyArgs.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
#include "gestures/GestureConverter.h"
#include "gestures/HardwareStateConverter.h"
#include "gestures/PropertyProvider.h"
@@ -54,6 +57,7 @@
void consumeGesture(const Gesture* gesture);
private:
+ void resetGestureInterpreter(nsecs_t when);
[[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs);
[[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
@@ -64,10 +68,19 @@
PropertyProvider mPropertyProvider;
+ // The MultiTouchMotionAccumulator is shared between the HardwareStateConverter and
+ // CapturedTouchpadEventConverter, so that if the touchpad is captured or released while touches
+ // are down, the relevant converter can still benefit from the current axis values stored in the
+ // accumulator.
+ MultiTouchMotionAccumulator mMotionAccumulator;
+
HardwareStateConverter mStateConverter;
GestureConverter mGestureConverter;
+ CapturedTouchpadEventConverter mCapturedEventConverter;
+ bool mPointerCaptured = false;
bool mProcessing = false;
+ bool mResettingInterpreter = false;
std::vector<Gesture> mGesturesToProcess;
};
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 8841b6e..6780dce 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -26,16 +26,11 @@
namespace android {
-HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext)
- : mDeviceContext(deviceContext), mTouchButtonAccumulator(deviceContext) {
- RawAbsoluteAxisInfo slotAxisInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
- if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
- ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
- "properly.",
- deviceContext.getName().c_str());
- }
- mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
+HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext,
+ MultiTouchMotionAccumulator& motionAccumulator)
+ : mDeviceContext(deviceContext),
+ mMotionAccumulator(motionAccumulator),
+ mTouchButtonAccumulator(deviceContext) {
mTouchButtonAccumulator.configure();
}
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index c314b0d..633448e 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -41,7 +41,8 @@
// Converts RawEvents into the HardwareState structs used by the gestures library.
class HardwareStateConverter {
public:
- HardwareStateConverter(const InputDeviceContext& deviceContext);
+ HardwareStateConverter(const InputDeviceContext& deviceContext,
+ MultiTouchMotionAccumulator& motionAccumulator);
std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent* event);
void reset();
@@ -51,7 +52,7 @@
const InputDeviceContext& mDeviceContext;
CursorButtonAccumulator mCursorButtonAccumulator;
- MultiTouchMotionAccumulator mMotionAccumulator;
+ MultiTouchMotionAccumulator& mMotionAccumulator;
TouchButtonAccumulator mTouchButtonAccumulator;
int32_t mMscTimestamp = 0;
};
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 97138c7..52277ff 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -39,6 +39,7 @@
srcs: [
"AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
+ "CapturedTouchpadEventConverter_test.cpp",
"EventHub_test.cpp",
"FakeEventHub.cpp",
"FakeInputReaderPolicy.cpp",
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
new file mode 100644
index 0000000..3dc5152
--- /dev/null
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -0,0 +1,784 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <CapturedTouchpadEventConverter.h>
+
+#include <list>
+#include <memory>
+
+#include <EventHub.h>
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+#include <linux/input.h>
+#include <utils/StrongPointer.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+#include "TestInputListenerMatchers.h"
+
+namespace android {
+
+using testing::AllOf;
+
+class CapturedTouchpadEventConverterTest : public testing::Test {
+public:
+ CapturedTouchpadEventConverterTest()
+ : mFakeEventHub(std::make_unique<FakeEventHub>()),
+ mFakePolicy(sp<FakeInputReaderPolicy>::make()),
+ mReader(mFakeEventHub, mFakePolicy, mFakeListener),
+ mDevice(newDevice()),
+ mDeviceContext(*mDevice, EVENTHUB_ID) {
+ const size_t slotCount = 8;
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0);
+ mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true);
+ }
+
+protected:
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ std::shared_ptr<InputDevice> newDevice() {
+ InputDeviceIdentifier identifier;
+ identifier.name = "device";
+ identifier.location = "USB1";
+ identifier.bus = 0;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader.getContext(), DEVICE_ID, /*generation=*/2,
+ identifier);
+ mReader.pushNextDevice(device);
+ mFakeEventHub->addDevice(EVENTHUB_ID, identifier.name, InputDeviceClass::TOUCHPAD,
+ identifier.bus);
+ mReader.loopOnce();
+ return device;
+ }
+
+ void addBasicAxesToEventHub() {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 1000, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, 0, 1000, 0, 0, 0);
+ }
+
+ CapturedTouchpadEventConverter createConverter() {
+ addBasicAxesToEventHub();
+ return CapturedTouchpadEventConverter(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+ }
+
+ void processAxis(CapturedTouchpadEventConverter& conv, int32_t type, int32_t code,
+ int32_t value) {
+ RawEvent event;
+ event.when = ARBITRARY_TIME;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ std::list<NotifyArgs> out = conv.process(event);
+ EXPECT_TRUE(out.empty());
+ }
+
+ std::list<NotifyArgs> processSync(CapturedTouchpadEventConverter& conv) {
+ RawEvent event;
+ event.when = ARBITRARY_TIME;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = EV_SYN;
+ event.code = SYN_REPORT;
+ event.value = 0;
+ return conv.process(event);
+ }
+
+ NotifyMotionArgs processSyncAndExpectSingleMotionArg(CapturedTouchpadEventConverter& conv) {
+ std::list<NotifyArgs> args = processSync(conv);
+ EXPECT_EQ(1u, args.size());
+ return std::get<NotifyMotionArgs>(args.front());
+ }
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ TestInputListener mFakeListener;
+ InstrumentedInputReader mReader;
+ std::shared_ptr<InputDevice> mDevice;
+ InputDeviceContext mDeviceContext;
+ MultiTouchMotionAccumulator mAccumulator;
+};
+
+TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_allAxesPresent_populatedCorrectly) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 1100, 0, 0, 35);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, 0, 1000, 0, 0, 30);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 900, 0, 0, 25);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, 0, 800, 0, 0, 20);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_ORIENTATION, -3, 4, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ InputDeviceInfo info;
+ conv.populateMotionRanges(info);
+
+ // Most axes should have min, max, and resolution matching the evdev axes.
+ const InputDeviceInfo::MotionRange* posX =
+ info.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, posX);
+ EXPECT_NEAR(0, posX->min, EPSILON);
+ EXPECT_NEAR(4000, posX->max, EPSILON);
+ EXPECT_NEAR(45, posX->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* posY =
+ info.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, posY);
+ EXPECT_NEAR(0, posY->min, EPSILON);
+ EXPECT_NEAR(2500, posY->max, EPSILON);
+ EXPECT_NEAR(40, posY->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* touchMajor =
+ info.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, touchMajor);
+ EXPECT_NEAR(0, touchMajor->min, EPSILON);
+ EXPECT_NEAR(1100, touchMajor->max, EPSILON);
+ EXPECT_NEAR(35, touchMajor->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* touchMinor =
+ info.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, touchMinor);
+ EXPECT_NEAR(0, touchMinor->min, EPSILON);
+ EXPECT_NEAR(1000, touchMinor->max, EPSILON);
+ EXPECT_NEAR(30, touchMinor->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* toolMajor =
+ info.getMotionRange(AMOTION_EVENT_AXIS_TOOL_MAJOR, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, toolMajor);
+ EXPECT_NEAR(0, toolMajor->min, EPSILON);
+ EXPECT_NEAR(900, toolMajor->max, EPSILON);
+ EXPECT_NEAR(25, toolMajor->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* toolMinor =
+ info.getMotionRange(AMOTION_EVENT_AXIS_TOOL_MINOR, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, toolMinor);
+ EXPECT_NEAR(0, toolMinor->min, EPSILON);
+ EXPECT_NEAR(800, toolMinor->max, EPSILON);
+ EXPECT_NEAR(20, toolMinor->resolution, EPSILON);
+
+ // ...except orientation and pressure, which get scaled, and size, which is generated from other
+ // values.
+ const InputDeviceInfo::MotionRange* orientation =
+ info.getMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, orientation);
+ EXPECT_NEAR(-M_PI_2, orientation->min, EPSILON);
+ EXPECT_NEAR(M_PI_2, orientation->max, EPSILON);
+ EXPECT_NEAR(0, orientation->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* pressure =
+ info.getMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, pressure);
+ EXPECT_NEAR(0, pressure->min, EPSILON);
+ EXPECT_NEAR(1, pressure->max, EPSILON);
+ EXPECT_NEAR(0, pressure->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* size =
+ info.getMotionRange(AMOTION_EVENT_AXIS_SIZE, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, size);
+ EXPECT_NEAR(0, size->min, EPSILON);
+ EXPECT_NEAR(1, size->max, EPSILON);
+ EXPECT_NEAR(0, size->resolution, EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_bareMinimumAxesPresent_populatedCorrectly) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ InputDeviceInfo info;
+ conv.populateMotionRanges(info);
+
+ // Only the bare minimum motion ranges should be reported, and no others (e.g. size shouldn't be
+ // present, since it's generated from axes that aren't provided by this device).
+ EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHPAD));
+ EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHPAD));
+ EXPECT_EQ(2u, info.getMotionRanges().size());
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OneFinger_motionReportedCorrectly) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(50, 100), WithToolType(ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 99);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithPointerCount(1u),
+ WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OneFinger_touchDimensionsPassedThrough) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 1000, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, 0, 1000, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MAJOR, 250);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MINOR, 120);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 400);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MINOR, 200);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithTouchDimensions(250, 120), WithToolDimensions(400, 200)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OneFinger_orientationCalculatedCorrectly) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_ORIENTATION, -3, 4, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_ORIENTATION, -3);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(-3 * M_PI / 8,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_ORIENTATION),
+ EPSILON);
+
+ processAxis(conv, EV_ABS, ABS_MT_ORIENTATION, 0);
+
+ EXPECT_NEAR(0,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_ORIENTATION),
+ EPSILON);
+
+ processAxis(conv, EV_ABS, ABS_MT_ORIENTATION, 4);
+
+ EXPECT_NEAR(M_PI / 2,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_ORIENTATION),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OneFinger_pressureScaledCorrectly) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_PRESSURE, 128);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), WithPressure(0.5));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest,
+ OneFinger_withAllSizeAxes_sizeCalculatedFromTouchMajorMinorAverage) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MAJOR, 138);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MINOR, 118);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 200);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MINOR, 210);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(0.5,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_SIZE),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest,
+ OneFinger_withMajorDimensionsOnly_sizeCalculatedFromTouchMajor) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MAJOR, 128);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 200);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(0.5,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_SIZE),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest,
+ OneFinger_withToolDimensionsOnly_sizeCalculatedFromToolMajorMinorAverage) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 138);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MINOR, 118);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(0.5,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_SIZE),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest,
+ OneFinger_withToolMajorOnly_sizeCalculatedFromTouchMajor) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 128);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(0.5,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_SIZE),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OnePalm_neverReported) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, FingerTurningIntoPalm_cancelled) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithToolType(ToolType::FINGER),
+ WithPointerCount(1u)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL), WithPointerCount(1u)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, PalmTurningIntoFinger_reported) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(51, 100)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(52, 100)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, FingerArrivingAfterPalm_onlyFingerReported) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 100);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 150);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(100, 150)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 102);
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 98);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 148);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(98, 148)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, FingerAndFingerTurningIntoPalm_partiallyCancelled) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 250);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerToolType(0, ToolType::FINGER),
+ WithPointerToolType(1, ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 251);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithFlags(AMOTION_EVENT_FLAG_CANCELED), WithPointerCount(2u)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, FingerAndPalmTurningIntoFinger_reported) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 250);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 251);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, TwoFingers_motionReportedCorrectly) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(50, 100), WithToolType(ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 99);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 250);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 200);
+
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerCoords(0, 52, 99),
+ WithPointerCoords(1, 250, 200), WithPointerToolType(0, ToolType::FINGER),
+ WithPointerToolType(1, ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 255);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 202);
+
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u),
+ WithPointerCoords(0, 52, 99), WithPointerCoords(1, 255, 202),
+ WithPointerToolType(1, ToolType::FINGER),
+ WithPointerToolType(0, ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerCoords(0, 52, 99),
+ WithPointerCoords(1, 255, 202), WithPointerToolType(0, ToolType::FINGER),
+ WithPointerToolType(1, ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(255, 202), WithToolType(ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithPointerCount(1u),
+ WithCoords(255, 202), WithToolType(ToolType::FINGER)));
+}
+
+// Pointer IDs max out at 31, and so must be reused once a touch is lifted to avoid running out.
+TEST_F(CapturedTouchpadEventConverterTest, PointerIdsReusedAfterLift) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ // Put down two fingers, which should get IDs 0 and 1.
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 10);
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 20);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithPointerId(/*index=*/0, /*id=*/0)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/0),
+ WithPointerId(/*index=*/1, /*id=*/1)));
+
+ // Lift the finger in slot 0, freeing up pointer ID 0...
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+
+ // ...and simultaneously add a finger in slot 2.
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 2);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 3);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 30);
+
+ args = processSync(conv);
+ ASSERT_EQ(3u, args.size());
+ // Slot 1 being present will result in a MOVE event, even though it hasn't actually moved (see
+ // comments in CapturedTouchpadEventConverter::sync).
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u),
+ WithPointerId(/*index=*/0, /*id=*/0), WithPointerId(/*index=*/1, /*id=*/1)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/0),
+ WithPointerId(/*index=*/1, /*id=*/1)));
+ args.pop_front();
+ // Slot 0 being lifted causes the finger from slot 1 to move up to index 0, but keep its
+ // previous ID. The new finger in slot 2 should take ID 0, which was just freed up.
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/1),
+ WithPointerId(/*index=*/1, /*id=*/0)));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 19d46c8..5bea2ba 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -25,6 +25,7 @@
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
#include "InstrumentedInputReader.h"
+#include "MultiTouchMotionAccumulator.h"
#include "TestConstants.h"
#include "TestInputListener.h"
@@ -38,8 +39,10 @@
mReader(mFakeEventHub, mFakePolicy, mFakeListener),
mDevice(newDevice()),
mDeviceContext(*mDevice, EVENTHUB_ID) {
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, 7, 0, 0, 0);
- mConverter = std::make_unique<HardwareStateConverter>(mDeviceContext);
+ const size_t slotCount = 8;
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0);
+ mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true);
+ mConverter = std::make_unique<HardwareStateConverter>(mDeviceContext, mAccumulator);
}
protected:
@@ -90,6 +93,7 @@
InstrumentedInputReader mReader;
std::shared_ptr<InputDevice> mDevice;
InputDeviceContext mDeviceContext;
+ MultiTouchMotionAccumulator mAccumulator;
std::unique_ptr<HardwareStateConverter> mConverter;
};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 147b7d4..a6cdee5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -62,6 +62,7 @@
static constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
static constexpr int32_t ACTION_UP = AMOTION_EVENT_ACTION_UP;
static constexpr int32_t ACTION_HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER;
+static constexpr int32_t ACTION_HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
static constexpr int32_t ACTION_HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT;
static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE;
static constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL;
@@ -2455,6 +2456,116 @@
}
/**
+ * Start hovering in a window. While this hover is still active, make another window appear on top.
+ * The top, obstructing window has no input channel, so it's not supposed to receive input.
+ * While the top window is present, the hovering is stopped.
+ * Later, hovering gets resumed again.
+ * Ensure that new hover gesture is handled correctly.
+ * This test reproduces a crash where the HOVER_EXIT event wasn't getting dispatched correctly
+ * to the window that's currently being hovered over.
+ */
+TEST_F(InputDispatcherTest, HoverWhileWindowAppears) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ // Only a single window is present at first
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Start hovering in the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now, an obscuring window appears!
+ sp<FakeWindowHandle> obscuringWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window",
+ ADISPLAY_ID_DEFAULT,
+ /*token=*/std::make_optional<sp<IBinder>>(nullptr));
+ obscuringWindow->setFrame(Rect(0, 0, 200, 200));
+ obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
+ obscuringWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
+ obscuringWindow->setNoInputChannel(true);
+ obscuringWindow->setFocusable(false);
+ obscuringWindow->setAlpha(1.0);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+
+ // While this new obscuring window is present, the hovering is stopped
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // Now the obscuring window goes away.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // And a new hover gesture starts.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+}
+
+/**
+ * Same test as 'HoverWhileWindowAppears' above, but here, we also send some HOVER_MOVE events to
+ * the obscuring window.
+ */
+TEST_F(InputDispatcherTest, HoverMoveWhileWindowAppears) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ // Only a single window is present at first
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Start hovering in the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now, an obscuring window appears!
+ sp<FakeWindowHandle> obscuringWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window",
+ ADISPLAY_ID_DEFAULT,
+ /*token=*/std::make_optional<sp<IBinder>>(nullptr));
+ obscuringWindow->setFrame(Rect(0, 0, 200, 200));
+ obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
+ obscuringWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
+ obscuringWindow->setNoInputChannel(true);
+ obscuringWindow->setFocusable(false);
+ obscuringWindow->setAlpha(1.0);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+
+ // While this new obscuring window is present, the hovering continues. The event can't go to the
+ // bottom window due to obstructed touches, so it should generate HOVER_EXIT for that window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ obscuringWindow->assertNoEvents();
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // Now the obscuring window goes away.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Hovering continues in the same position. The hovering pointer re-enters the bottom window,
+ // so it should generate a HOVER_ENTER
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now the MOVE should be getting dispatched normally
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+}
+
+/**
* Two windows: a window on the left and a window on the right.
* Mouse is clicked on the left window and remains down. Touch is touched on the right and remains
* down. Then, on the left window, also place second touch pointer down.
@@ -3446,7 +3557,9 @@
.build()));
window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ // We already canceled the hovering implicitly by injecting the "DOWN" event without lifting the
+ // hover first. Therefore, injection of HOVER_EXIT is inconsistent, and should fail.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
AINPUT_SOURCE_MOUSE)
@@ -3832,7 +3945,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -3846,7 +3959,7 @@
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
// All windows are removed from the display. Ensure that we can no longer dispatch to it.
- mDispatcher->onWindowInfosChanged({}, {});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
window->consumeFocusEvent(false);
@@ -3862,7 +3975,7 @@
// Ensure window is non-split and have some transform.
window->setPreventSplitting(true);
window->setWindowOffset(20, 40);
- mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
@@ -3909,12 +4022,12 @@
info.displayId = displayId;
info.transform = transform;
mDisplayInfos.push_back(std::move(info));
- mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
+ mDispatcher->onWindowInfosChanged({mWindowInfos, mDisplayInfos, 0, 0});
}
void addWindow(const sp<WindowInfoHandle>& windowHandle) {
mWindowInfos.push_back(*windowHandle->getInfo());
- mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
+ mDispatcher->onWindowInfosChanged({mWindowInfos, mDisplayInfos, 0, 0});
}
void removeAllWindowsAndDisplays() {
@@ -5102,7 +5215,7 @@
displayInfo.displayId = ADISPLAY_ID_DEFAULT;
displayInfo.transform = transform;
- mDispatcher->onWindowInfosChanged({*window->getInfo()}, {displayInfo});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {displayInfo}, 0, 0});
const NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -5872,7 +5985,7 @@
displayInfos[1].displayId = SECOND_DISPLAY_ID;
displayInfos[1].transform = secondDisplayTransform;
- mDispatcher->onWindowInfosChanged({}, displayInfos);
+ mDispatcher->onWindowInfosChanged({{}, displayInfos, 0, 0});
// Enable InputFilter
mDispatcher->setInputFilterEnabled(true);
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 338b747..db6f254 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -73,6 +73,12 @@
return arg.pointerCount == count;
}
+MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
+ const auto argPointerId = arg.pointerProperties[index].id;
+ *result_listener << "expected pointer with index " << index << " to have ID " << argPointerId;
+ return argPointerId == id;
+}
+
MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
const auto argX = arg.pointerCoords[0].getX();
const auto argY = arg.pointerCoords[0].getY();
@@ -136,6 +142,22 @@
return argPressure == pressure;
}
+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);
+ *result_listener << "expected touch dimensions " << maj << " major x " << min
+ << " minor, but got " << argMajor << " major x " << argMinor << " minor";
+ return argMajor == maj && argMinor == min;
+}
+
+MATCHER_P2(WithToolDimensions, maj, min, "InputEvent with specified tool dimensions") {
+ const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR);
+ const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR);
+ *result_listener << "expected tool dimensions " << maj << " major x " << min
+ << " minor, but got " << argMajor << " major x " << argMinor << " minor";
+ return argMajor == maj && argMinor == min;
+}
+
MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
const auto argToolType = arg.pointerProperties[0].toolType;
*result_listener << "expected tool type " << ftl::enum_string(toolType) << ", but got "
@@ -143,6 +165,14 @@
return argToolType == toolType;
}
+MATCHER_P2(WithPointerToolType, pointer, toolType,
+ "InputEvent with specified tool type for pointer") {
+ const auto argToolType = arg.pointerProperties[pointer].toolType;
+ *result_listener << "expected pointer " << pointer << " to have tool type "
+ << ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
+ return argToolType == toolType;
+}
+
MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
*result_listener << "expected flags " << flags << ", but got " << arg.flags;
return arg.flags == static_cast<int32_t>(flags);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index bfb7a22..3371ae2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -254,8 +254,7 @@
mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
}
if (mHadClonedChild) {
- auto& roots = mFlinger->mLayerMirrorRoots;
- roots.erase(std::remove(roots.begin(), roots.end(), this), roots.end());
+ mFlinger->mNumClones--;
}
if (hasTrustedPresentationListener()) {
mFlinger->mNumTrustedPresentationListeners--;
@@ -2595,7 +2594,7 @@
mDrawingState.inputInfo = tmpInputInfo;
}
-bool Layer::updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates) {
+void Layer::updateMirrorInfo() {
if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) {
// If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false,
// it means that there is a clone, but the layer it was cloned from has been destroyed. In
@@ -2603,7 +2602,7 @@
// destroyed. The root, this layer, will still be around since the client can continue
// to hold a reference, but no cloned layers will be displayed.
mClonedChild = nullptr;
- return true;
+ return;
}
std::map<sp<Layer>, sp<Layer>> clonedLayersMap;
@@ -2618,13 +2617,6 @@
mClonedChild->updateClonedDrawingState(clonedLayersMap);
mClonedChild->updateClonedChildren(sp<Layer>::fromExisting(this), clonedLayersMap);
mClonedChild->updateClonedRelatives(clonedLayersMap);
-
- for (Layer* root : cloneRootsPendingUpdates) {
- if (clonedLayersMap.find(sp<Layer>::fromExisting(root)) != clonedLayersMap.end()) {
- return false;
- }
- }
- return true;
}
void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
@@ -2772,7 +2764,7 @@
void Layer::setClonedChild(const sp<Layer>& clonedChild) {
mClonedChild = clonedChild;
mHadClonedChild = true;
- mFlinger->mLayerMirrorRoots.push_back(this);
+ mFlinger->mNumClones++;
}
bool Layer::setDropInputMode(gui::DropInputMode mode) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4374914..2640c92 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -651,7 +651,7 @@
gui::WindowInfo::Type getWindowType() const { return mWindowType; }
- bool updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates);
+ void updateMirrorInfo();
/*
* doTransaction - process the transaction. This is a good place to figure
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index d606cff..f6b5afc 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -85,7 +85,7 @@
// If layer is offscreen, update mirroring info if it exists
if (mLayer->isRemovedFromCurrentState()) {
mLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->updateMirrorInfo({}); });
+ [&](Layer* layer) { layer->updateMirrorInfo(); });
mLayer->traverse(LayerVector::StateSet::Drawing,
[&](Layer* layer) { layer->updateCloneBufferInfo(); });
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b5ba94e..3c55caf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2544,7 +2544,7 @@
}
updateCursorAsync();
- updateInputFlinger();
+ updateInputFlinger(vsyncId);
if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and tracing should only be enabled for debugging.
@@ -3732,7 +3732,7 @@
doCommitTransactions();
}
-void SurfaceFlinger::updateInputFlinger() {
+void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId) {
if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
return;
}
@@ -3744,6 +3744,8 @@
if (mUpdateInputInfo) {
mUpdateInputInfo = false;
updateWindowInfo = true;
+ mLastInputFlingerUpdateVsyncId = vsyncId;
+ mLastInputFlingerUpdateTimestamp = systemTime();
buildWindowInfos(windowInfos, displayInfos);
}
@@ -3773,7 +3775,9 @@
std::move(
inputWindowCommands.windowInfosReportedListeners),
/* forceImmediateCall= */ visibleWindowsChanged ||
- !inputWindowCommands.focusRequests.empty());
+ !inputWindowCommands.focusRequests.empty(),
+ mLastInputFlingerUpdateVsyncId,
+ mLastInputFlingerUpdateTimestamp);
} else {
// If there are listeners but no changes to input windows, call the listeners
// immediately.
@@ -4020,21 +4024,8 @@
}
commitOffscreenLayers();
- if (mLayerMirrorRoots.size() > 0) {
- std::deque<Layer*> pendingUpdates;
- pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(),
- mLayerMirrorRoots.end());
- std::vector<Layer*> needsUpdating;
- for (Layer* cloneRoot : mLayerMirrorRoots) {
- pendingUpdates.pop_front();
- if (cloneRoot->updateMirrorInfo(pendingUpdates)) {
- } else {
- needsUpdating.push_back(cloneRoot);
- }
- }
- for (Layer* cloneRoot : needsUpdating) {
- cloneRoot->updateMirrorInfo({});
- }
+ if (mNumClones > 0) {
+ mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
}
}
@@ -4141,7 +4132,7 @@
mBootStage = BootStage::BOOTANIMATION;
}
- if (mLayerMirrorRoots.size() > 0) {
+ if (mNumClones > 0) {
mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
}
@@ -6130,6 +6121,29 @@
result.append(mTimeStats->miniDump());
result.append("\n");
+
+ result.append("Window Infos:\n");
+ StringAppendF(&result, " input flinger update vsync id: %" PRId64 "\n",
+ mLastInputFlingerUpdateVsyncId.value);
+ StringAppendF(&result, " input flinger update timestamp (ns): %" PRId64 "\n",
+ mLastInputFlingerUpdateTimestamp);
+ result.append("\n");
+
+ if (int64_t unsentVsyncId = mWindowInfosListenerInvoker->getUnsentMessageVsyncId().value;
+ unsentVsyncId != -1) {
+ StringAppendF(&result, " unsent input flinger update vsync id: %" PRId64 "\n",
+ unsentVsyncId);
+ StringAppendF(&result, " unsent input flinger update timestamp (ns): %" PRId64 "\n",
+ mWindowInfosListenerInvoker->getUnsentMessageTimestamp());
+ result.append("\n");
+ }
+
+ if (uint32_t pendingMessages = mWindowInfosListenerInvoker->getPendingMessageCount();
+ pendingMessages != 0) {
+ StringAppendF(&result, " pending input flinger calls: %" PRIu32 "\n",
+ mWindowInfosListenerInvoker->getPendingMessageCount());
+ result.append("\n");
+ }
}
mat4 SurfaceFlinger::calculateColorMatrix(float saturation) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 92e19c0..8eaa1c7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -296,7 +296,8 @@
// the client can no longer modify this layer directly.
void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
- std::vector<Layer*> mLayerMirrorRoots;
+ // TODO: Remove atomic if move dtor to main thread CL lands
+ std::atomic<uint32_t> mNumClones;
TransactionCallbackInvoker& getTransactionCallbackInvoker() {
return mTransactionCallbackInvoker;
@@ -716,7 +717,7 @@
void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
- void updateInputFlinger();
+ void updateInputFlinger(VsyncId);
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
std::vector<gui::DisplayInfo>& outDisplayInfos);
@@ -1247,6 +1248,9 @@
VsyncId mLastCommittedVsyncId;
+ VsyncId mLastInputFlingerUpdateVsyncId;
+ nsecs_t mLastInputFlingerUpdateTimestamp;
+
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 856fbbb..2b62638 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -16,6 +16,7 @@
#include <ftl/small_vector.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/WindowInfosUpdate.h>
#include "WindowInfosListenerInvoker.h"
@@ -86,11 +87,12 @@
void WindowInfosListenerInvoker::windowInfosChanged(
std::vector<WindowInfo> windowInfos, std::vector<DisplayInfo> displayInfos,
- WindowInfosReportedListenerSet reportedListeners, bool forceImmediateCall) {
+ WindowInfosReportedListenerSet reportedListeners, bool forceImmediateCall, VsyncId vsyncId,
+ nsecs_t timestamp) {
reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
auto callListeners = [this, windowInfos = std::move(windowInfos),
- displayInfos = std::move(displayInfos)](
- WindowInfosReportedListenerSet reportedListeners) mutable {
+ displayInfos = std::move(displayInfos), vsyncId,
+ timestamp](WindowInfosReportedListenerSet reportedListeners) mutable {
WindowInfosListenerVector windowInfosListeners;
{
std::scoped_lock lock(mListenersMutex);
@@ -103,6 +105,9 @@
sp<WindowInfosReportedListenerInvoker>::make(windowInfosListeners,
std::move(reportedListeners));
+ gui::WindowInfosUpdate update(std::move(windowInfos), std::move(displayInfos),
+ vsyncId.value, timestamp);
+
for (const auto& listener : windowInfosListeners) {
sp<IBinder> asBinder = IInterface::asBinder(listener);
@@ -111,8 +116,7 @@
// calling onWindowInfosReported.
asBinder->linkToDeath(reportedInvoker);
- auto status =
- listener->onWindowInfosChanged(windowInfos, displayInfos, reportedInvoker);
+ auto status = listener->onWindowInfosChanged(update, reportedInvoker);
if (!status.isOk()) {
reportedInvoker->onWindowInfosReported();
}
@@ -129,11 +133,15 @@
// to reduce the amount of binder memory used.
if (mActiveMessageCount > 0 && !forceImmediateCall) {
mWindowInfosChangedDelayed = std::move(callListeners);
+ mUnsentVsyncId = vsyncId;
+ mUnsentTimestamp = timestamp;
mReportedListenersDelayed.merge(reportedListeners);
return;
}
mWindowInfosChangedDelayed = nullptr;
+ mUnsentVsyncId = {-1};
+ mUnsentTimestamp = -1;
reportedListeners.merge(mReportedListenersDelayed);
mActiveMessageCount++;
}
@@ -154,6 +162,8 @@
mActiveMessageCount++;
callListeners = std::move(mWindowInfosChangedDelayed);
mWindowInfosChangedDelayed = nullptr;
+ mUnsentVsyncId = {-1};
+ mUnsentTimestamp = -1;
reportedListeners = std::move(mReportedListenersDelayed);
mReportedListenersDelayed.clear();
}
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index 4da9828..e35d056 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -26,6 +26,8 @@
#include <gui/SpHash.h>
#include <utils/Mutex.h>
+#include "scheduler/VsyncId.h"
+
namespace android {
using WindowInfosReportedListenerSet =
@@ -40,10 +42,25 @@
void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>,
WindowInfosReportedListenerSet windowInfosReportedListeners,
- bool forceImmediateCall);
+ bool forceImmediateCall, VsyncId vsyncId, nsecs_t timestamp);
binder::Status onWindowInfosReported() override;
+ VsyncId getUnsentMessageVsyncId() {
+ std::scoped_lock lock(mMessagesMutex);
+ return mUnsentVsyncId;
+ }
+
+ nsecs_t getUnsentMessageTimestamp() {
+ std::scoped_lock lock(mMessagesMutex);
+ return mUnsentTimestamp;
+ }
+
+ uint32_t getPendingMessageCount() {
+ std::scoped_lock lock(mMessagesMutex);
+ return mActiveMessageCount;
+ }
+
protected:
void binderDied(const wp<IBinder>& who) override;
@@ -58,6 +75,8 @@
uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
std::function<void(WindowInfosReportedListenerSet)> mWindowInfosChangedDelayed
GUARDED_BY(mMessagesMutex);
+ VsyncId mUnsentVsyncId GUARDED_BY(mMessagesMutex) = {-1};
+ nsecs_t mUnsentTimestamp GUARDED_BY(mMessagesMutex) = -1;
WindowInfosReportedListenerSet mReportedListenersDelayed;
};
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index c1bab0e..4d13aca 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -590,7 +590,7 @@
mFlinger->binderDied(display);
mFlinger->onFirstRef();
- mFlinger->updateInputFlinger();
+ mFlinger->updateInputFlinger(VsyncId{0});
mFlinger->updateCursorAsync();
mutableScheduler().setVsyncConfig({.sfOffset = mFdp.ConsumeIntegral<nsecs_t>(),
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index f4a8f03..3f27360 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/WindowInfosUpdate.h>
#include <private/android_filesystem_config.h>
#include <cstdint>
#include <future>
@@ -41,9 +42,8 @@
WindowInfosListener(WindowInfosPredicate predicate, std::promise<void>& promise)
: mPredicate(std::move(predicate)), mPromise(promise) {}
- void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
- const std::vector<DisplayInfo>&) override {
- if (mPredicate(windowInfos)) {
+ void onWindowInfosChanged(const gui::WindowInfosUpdate& update) override {
+ if (mPredicate(update.windowInfos)) {
mPromise.set_value();
}
}