Merge "Rename small dirty detection flag" into main
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index fd2b27f..620c23c 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -359,6 +359,38 @@
}
cc_library_static {
+ name: "libbinder_rpc_no_blob",
+ vendor_available: true,
+ defaults: [
+ "libbinder_common_defaults",
+ "libbinder_android_defaults",
+ "libbinder_kernel_defaults",
+ ],
+ cflags: [
+ "-DBINDER_DISABLE_BLOB",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
+ name: "libbinder_rpc_no_native_handle",
+ vendor_available: true,
+ defaults: [
+ "libbinder_common_defaults",
+ "libbinder_android_defaults",
+ "libbinder_kernel_defaults",
+ ],
+ cflags: [
+ "-DBINDER_DISABLE_NATIVE_HANDLE",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
name: "libbinder_rpc_single_threaded",
defaults: [
"libbinder_common_defaults",
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index b78b0b3..a3ff7d2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -40,7 +40,9 @@
#include <binder/TextOutput.h>
#include <android-base/scopeguard.h>
+#ifndef BINDER_DISABLE_BLOB
#include <cutils/ashmem.h>
+#endif
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/String16.h>
@@ -1475,6 +1477,7 @@
return writeParcelable(*parcelable);
}
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
status_t Parcel::writeNativeHandle(const native_handle* handle)
{
if (!handle || handle->version != sizeof(native_handle))
@@ -1497,6 +1500,7 @@
err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts);
return err;
}
+#endif
status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) {
if (auto* rpcFields = maybeRpcFields()) {
@@ -1589,6 +1593,12 @@
status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob)
{
+#ifdef BINDER_DISABLE_BLOB
+ (void)len;
+ (void)mutableCopy;
+ (void)outBlob;
+ return INVALID_OPERATION;
+#else
if (len > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
@@ -1640,6 +1650,7 @@
}
::close(fd);
return status;
+#endif
}
status_t Parcel::writeDupImmutableBlobFileDescriptor(int fd)
@@ -2271,6 +2282,7 @@
return status.exceptionCode();
}
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
native_handle* Parcel::readNativeHandle() const
{
int numFds, numInts;
@@ -2303,6 +2315,7 @@
}
return h;
}
+#endif
int Parcel::readFileDescriptor() const {
if (const auto* rpcFields = maybeRpcFields()) {
@@ -2423,6 +2436,11 @@
status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const
{
+#ifdef BINDER_DISABLE_BLOB
+ (void)len;
+ (void)outBlob;
+ return INVALID_OPERATION;
+#else
int32_t blobType;
status_t status = readInt32(&blobType);
if (status) return status;
@@ -2456,6 +2474,7 @@
outBlob->init(fd, ptr, len, isMutable);
return NO_ERROR;
+#endif
}
status_t Parcel::read(FlattenableHelperInterface& val) const
@@ -3204,6 +3223,7 @@
}
size_t openAshmemSize = 0;
+#ifndef BINDER_DISABLE_BLOB
for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
const flat_binder_object* flat =
reinterpret_cast<const flat_binder_object*>(mData + kernelFields->mObjects[i]);
@@ -3218,6 +3238,7 @@
}
}
}
+#endif
return openAshmemSize;
}
#endif // BINDER_WITH_KERNEL_IPC
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b94267c..98d12bb 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -26,7 +26,9 @@
#include <vector>
#include <android-base/unique_fd.h>
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
#include <cutils/native_handle.h>
+#endif
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/String16.h>
@@ -324,11 +326,13 @@
template<typename T>
status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
// Place a native_handle into the parcel (the native_handle's file-
// descriptors are dup'ed, so it is safe to delete the native_handle
// when this function returns).
// Doesn't take ownership of the native_handle.
status_t writeNativeHandle(const native_handle* handle);
+#endif
// Place a file descriptor into the parcel. The given fd must remain
// valid for the lifetime of the parcel.
@@ -559,13 +563,14 @@
// response headers rather than doing it by hand.
int32_t readExceptionCode() const;
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
// Retrieve native_handle from the parcel. This returns a copy of the
// parcel's native_handle (the caller takes ownership). The caller
- // must free the native_handle with native_handle_close() and
+ // must free the native_handle with native_handle_close() and
// native_handle_delete().
native_handle* readNativeHandle() const;
+#endif
-
// Retrieve a file descriptor from the parcel. This returns the raw fd
// in the parcel, which you do not own -- use dup() to get your own copy.
int readFileDescriptor() const;
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 8841fe6..ed870b6 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -144,6 +144,7 @@
#[doc(hidden)]
pub mod unstable_api {
pub use crate::binder::AsNative;
+ pub use crate::error::status_result;
pub use crate::proxy::unstable_api::new_spibinder;
pub use crate::sys::AIBinder;
pub use crate::sys::AParcel;
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index dc1575c..90d0a8e 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -19,7 +19,7 @@
}
rust_bindgen {
- name: "libnativewindow_bindgen",
+ name: "libnativewindow_bindgen_internal",
crate_name: "nativewindow_bindgen",
wrapper_src: "sys/nativewindow_bindings.h",
source_stem: "bindings",
@@ -28,13 +28,21 @@
"--bitfield-enum=AHardwareBuffer_UsageFlags",
"--allowlist-file=.*/nativewindow/include/.*\\.h",
+ "--blocklist-type",
+ "AParcel",
+ "--raw-line",
+ "use binder::unstable_api::AParcel;",
"--with-derive-eq",
"--with-derive-partialeq",
],
shared_libs: [
+ "libbinder_ndk",
"libnativewindow",
],
+ rustlibs: [
+ "libbinder_rs",
+ ],
// Currently necessary for host builds
// TODO(b/31559095): bionic on host should define this
@@ -44,12 +52,40 @@
},
},
min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
+}
+
+rust_library {
+ name: "libnativewindow_bindgen",
+ crate_name: "nativewindow_bindgen",
+ srcs: [":libnativewindow_bindgen_internal"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libnativewindow",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+ lints: "none",
+ clippy_lints: "none",
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
}
rust_test {
name: "libnativewindow_bindgen_test",
- srcs: [":libnativewindow_bindgen"],
+ srcs: [":libnativewindow_bindgen_internal"],
crate_name: "nativewindow_bindgen_test",
+ rustlibs: [
+ "libbinder_rs",
+ ],
test_suites: ["general-tests"],
auto_gen_config: true,
clippy_lints: "none",
@@ -60,6 +96,7 @@
name: "libnativewindow_defaults",
srcs: ["src/lib.rs"],
rustlibs: [
+ "libbinder_rs",
"libnativewindow_bindgen",
],
}
@@ -77,6 +114,7 @@
},
},
min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
}
rust_test {
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 6eb3bbc..6f86c4a 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,13 +16,22 @@
extern crate nativewindow_bindgen as ffi;
-pub use ffi::{AHardwareBuffer, AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+use binder::{
+ binder_impl::{
+ BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize,
+ SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
+ },
+ unstable_api::{status_result, AsNative},
+ StatusCode,
+};
+use ffi::{AHardwareBuffer, AHardwareBuffer_readFromParcel, AHardwareBuffer_writeToParcel};
use std::fmt::{self, Debug, Formatter};
use std::mem::ManuallyDrop;
-use std::ptr::{self, NonNull};
+use std::ptr::{self, null_mut, NonNull};
-/// Wrapper around an opaque C AHardwareBuffer.
+/// Wrapper around an opaque C `AHardwareBuffer`.
#[derive(PartialEq, Eq)]
pub struct HardwareBuffer(NonNull<AHardwareBuffer>);
@@ -120,8 +129,11 @@
/// Available since API level 31.
pub fn id(&self) -> u64 {
let mut out_id = 0;
- // SAFETY: Neither pointers can be null.
- let status = unsafe { ffi::AHardwareBuffer_getId(self.0.as_ref(), &mut out_id) };
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it. The id pointer must be valid because it comes from a reference.
+ let status = unsafe { ffi::AHardwareBuffer_getId(self.0.as_ptr(), &mut out_id) };
assert_eq!(status, 0, "id() failed for AHardwareBuffer with error code: {status}");
out_id
@@ -176,9 +188,10 @@
impl Drop for HardwareBuffer {
fn drop(&mut self) {
- // SAFETY: self.0 will never be null. AHardwareBuffers allocated from within Rust will have
- // a refcount of one, and there is a safety warning on taking an AHardwareBuffer from a raw
- // pointer requiring callers to ensure the refcount is managed appropriately.
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it.
unsafe { ffi::AHardwareBuffer_release(self.0.as_ptr()) }
}
}
@@ -197,6 +210,82 @@
}
}
+impl Serialize for HardwareBuffer {
+ fn serialize(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
+ SerializeOption::serialize_option(Some(self), parcel)
+ }
+}
+
+impl SerializeOption for HardwareBuffer {
+ fn serialize_option(
+ this: Option<&Self>,
+ parcel: &mut BorrowedParcel,
+ ) -> Result<(), StatusCode> {
+ if let Some(this) = this {
+ parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
+
+ let status =
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it.
+ unsafe { AHardwareBuffer_writeToParcel(this.0.as_ptr(), parcel.as_native_mut()) };
+ status_result(status)
+ } else {
+ parcel.write(&NULL_PARCELABLE_FLAG)
+ }
+ }
+}
+
+impl Deserialize for HardwareBuffer {
+ type UninitType = Option<Self>;
+
+ fn uninit() -> Option<Self> {
+ None
+ }
+
+ fn from_init(value: Self) -> Option<Self> {
+ Some(value)
+ }
+
+ fn deserialize(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
+ DeserializeOption::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl DeserializeOption for HardwareBuffer {
+ fn deserialize_option(parcel: &BorrowedParcel) -> Result<Option<Self>, StatusCode> {
+ let present: i32 = parcel.read()?;
+ match present {
+ NULL_PARCELABLE_FLAG => Ok(None),
+ NON_NULL_PARCELABLE_FLAG => {
+ let mut buffer = null_mut();
+
+ let status =
+ // SAFETY: Both pointers must be valid because they are obtained from references.
+ // `AHardwareBuffer_readFromParcel` doesn't store them or do anything else special
+ // with them. If it returns success then it will have allocated a new
+ // `AHardwareBuffer` and incremented the reference count, so we can use it until we
+ // release it.
+ unsafe { AHardwareBuffer_readFromParcel(parcel.as_native(), &mut buffer) };
+
+ status_result(status)?;
+
+ Ok(Some(Self(NonNull::new(buffer).expect(
+ "AHardwareBuffer_readFromParcel returned success but didn't allocate buffer",
+ ))))
+ }
+ _ => Err(StatusCode::BAD_VALUE),
+ }
+ }
+}
+
+impl SerializeArray for HardwareBuffer {}
+
+impl DeserializeArray for HardwareBuffer {}
+
// SAFETY: The underlying *AHardwareBuffers can be moved between threads.
unsafe impl Send for HardwareBuffer {}
diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h
index e652aee..4525a42 100644
--- a/libs/nativewindow/rust/sys/nativewindow_bindings.h
+++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h
@@ -16,5 +16,6 @@
#include <android/data_space.h>
#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_aidl.h>
#include <android/hdr_metadata.h>
#include <android/native_window.h>
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
index f24a4f1..5bf6560 100644
--- a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
@@ -18,7 +18,6 @@
#include "SkiaMemoryReporter.h"
-#include <SkString.h>
#include <android-base/stringprintf.h>
#include <log/log_main.h>
@@ -142,7 +141,7 @@
TraceValue traceValue = convertUnits(result->second);
const char* entry = (traceValue.count > 1) ? "entries" : "entry";
StringAppendF(&log, " %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(),
- traceValue.value, traceValue.units, traceValue.count, entry);
+ traceValue.value, traceValue.units.c_str(), traceValue.count, entry);
}
if (mItemize) {
for (const auto& individualItem : resultsMap) {
@@ -153,7 +152,7 @@
auto result = individualItem.second.find("size");
TraceValue size = convertUnits(result->second);
StringAppendF(&log, " %s: size[%.2f %s]", individualItem.first.c_str(),
- size.value, size.units);
+ size.value, size.units.c_str());
if (!wrappedResources) {
for (const auto& itemValues : individualItem.second) {
if (strcmp("size", itemValues.first) == 0) {
@@ -162,10 +161,10 @@
TraceValue traceValue = convertUnits(itemValues.second);
if (traceValue.value == 0.0f) {
StringAppendF(&log, " %s[%s]", itemValues.first,
- traceValue.units);
+ traceValue.units.c_str());
} else {
StringAppendF(&log, " %s[%.2f %s]", itemValues.first,
- traceValue.value, traceValue.units);
+ traceValue.value, traceValue.units.c_str());
}
}
}
@@ -184,16 +183,16 @@
TraceValue total = convertUnits(mTotalSize);
TraceValue purgeable = convertUnits(mPurgeableSize);
StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
- total.value, total.units, purgeable.value, purgeable.units);
+ total.value, total.units.c_str(), purgeable.value, purgeable.units.c_str());
}
SkiaMemoryReporter::TraceValue SkiaMemoryReporter::convertUnits(const TraceValue& value) {
TraceValue output(value);
- if (SkString("bytes") == SkString(output.units) && output.value >= 1024) {
+ if (SkString("bytes") == output.units && output.value >= 1024) {
output.value = output.value / 1024.0f;
output.units = "KB";
}
- if (SkString("KB") == SkString(output.units) && output.value >= 1024) {
+ if (SkString("KB") == output.units && output.value >= 1024) {
output.value = output.value / 1024.0f;
output.units = "MB";
}
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.h b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
index dbbd65b..da91674 100644
--- a/libs/renderengine/skia/debug/SkiaMemoryReporter.h
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
@@ -16,6 +16,7 @@
#pragma once
+#include <SkString.h>
#include <SkTraceMemoryDump.h>
#include <string>
@@ -75,7 +76,7 @@
TraceValue(const char* units, uint64_t value) : units(units), value(value), count(1) {}
TraceValue(const TraceValue& v) : units(v.units), value(v.value), count(v.count) {}
- const char* units;
+ SkString units;
float value;
int count;
};
@@ -104,4 +105,4 @@
} /* namespace skia */
} /* namespace renderengine */
-} /* namespace android */
\ No newline at end of file
+} /* namespace android */
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 45649dd..7dfbf94 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -634,11 +634,10 @@
const MotionEntry& entry) {
std::vector<TouchedWindow> out;
const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
- if (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER &&
- maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE &&
- maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
- // Not a hover event - don't need to do anything
- return out;
+
+ if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ // ACTION_SCROLL events should not affect the hovering pointer dispatch
+ return {};
}
// We should consider all hovering pointers here. But for now, just use the first one
@@ -1315,8 +1314,9 @@
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
- addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds,
- /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
+ addPointerWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
+ pointerIds,
+ /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
}
}
return outsideTargets;
@@ -1821,7 +1821,7 @@
std::vector<InputTarget> inputTargets;
addWindowTargetLocked(focusedWindow,
InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS,
- /*pointerIds=*/{}, getDownTime(*entry), inputTargets);
+ getDownTime(*entry), inputTargets);
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
@@ -1905,7 +1905,6 @@
// Identify targets.
std::vector<InputTarget> inputTargets;
- bool conflictingPointerActions = false;
InputEventInjectionResult injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
@@ -1917,8 +1916,7 @@
}
inputTargets =
- findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
- /*byref*/ injectionResult);
+ findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult);
LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED &&
!inputTargets.empty());
} else {
@@ -1930,7 +1928,7 @@
addWindowTargetLocked(focusedWindow,
InputTarget::Flags::FOREGROUND |
InputTarget::Flags::DISPATCH_AS_IS,
- /*pointerIds=*/{}, getDownTime(*entry), inputTargets);
+ getDownTime(*entry), inputTargets);
}
}
if (injectionResult == InputEventInjectionResult::PENDING) {
@@ -1954,11 +1952,6 @@
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
// Dispatch the motion.
- if (conflictingPointerActions) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "conflicting pointer actions");
- synthesizeCancelationEventsForAllConnectionsLocked(options);
- }
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
@@ -2258,7 +2251,7 @@
}
std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
+ nsecs_t currentTime, const MotionEntry& entry,
InputEventInjectionResult& outInjectionResult) {
ATRACE_CALL();
@@ -2283,20 +2276,13 @@
}
bool isSplit = shouldSplitTouch(tempTouchState, entry);
- bool switchedDevice = false;
- if (oldState != nullptr) {
- std::set<int32_t> oldActiveDevices = oldState->getActiveDeviceIds();
- const bool anotherDeviceIsActive =
- oldActiveDevices.count(entry.deviceId) == 0 && !oldActiveDevices.empty();
- switchedDevice |= anotherDeviceIsActive;
- }
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
// A DOWN could be generated from POINTER_DOWN if the initial pointers did not land into any
// touchable windows.
- const bool wasDown = oldState != nullptr && oldState->isDown();
+ const bool wasDown = oldState != nullptr && oldState->isDown(entry.deviceId);
const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
(maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
@@ -2304,34 +2290,19 @@
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
- // from another device. However, if the new event is a down event, let's cancel the current
- // touch and let the new one take over.
- if (switchedDevice && wasDown && !isDown) {
- LOG(INFO) << "Dropping event because a pointer for another device "
- << " is already down in display " << displayId << ": " << entry.getDescription();
- // TODO(b/211379801): test multiple simultaneous input streams.
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {}; // wrong device
+ if (newGesture) {
+ isSplit = false;
}
- if (newGesture) {
- // If a new gesture is starting, clear the touch state completely.
- tempTouchState.reset();
- isSplit = false;
- } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
- ALOGI("Dropping move event because a pointer for a different device is already active "
- "in display %" PRId32,
- displayId);
- // TODO(b/211379801): test multiple simultaneous input streams.
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {}; // wrong device
+ if (isDown && tempTouchState.hasHoveringPointers(entry.deviceId)) {
+ // Compatibility behaviour: ACTION_DOWN causes HOVER_EXIT to get generated.
+ tempTouchState.clearHoveringPointers(entry.deviceId);
}
if (isHoverAction) {
// For hover actions, we will treat 'tempTouchState' as a new state, so let's erase
// all of the existing hovering pointers and recompute.
- tempTouchState.clearHoveringPointers();
+ tempTouchState.clearHoveringPointers(entry.deviceId);
}
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
@@ -2402,8 +2373,7 @@
continue;
}
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ if (isHoverAction) {
// The "windowHandle" is the target of this hovering pointer.
tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
}
@@ -2426,31 +2396,24 @@
}
// Update the temporary touch state.
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
+
if (!isHoverAction) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
- }
-
- const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
- maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
-
- // TODO(b/211379801): Currently, even if pointerIds are empty (hover case), we would
- // still add a window to the touch state. We should avoid doing that, but some of the
- // later checks ("at least one foreground window") rely on this in order to dispatch
- // the event properly, so that needs to be updated, possibly by looking at InputTargets.
- tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId, pointerIds,
- isDownOrPointerDown
- ? std::make_optional(entry.eventTime)
- : std::nullopt);
-
- // If this is the pointer going down and the touched window has a wallpaper
- // then also add the touched wallpaper windows so they are locked in for the duration
- // of the touch gesture.
- // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
- // engine only supports touch events. We would need to add a mechanism similar
- // to View.onGenericMotionEvent to enable wallpapers to handle these events.
- if (isDownOrPointerDown) {
- if (targetFlags.test(InputTarget::Flags::FOREGROUND) &&
+ const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
+ maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
+ tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId,
+ pointerIds,
+ isDownOrPointerDown
+ ? std::make_optional(entry.eventTime)
+ : std::nullopt);
+ // If this is the pointer going down and the touched window has a wallpaper
+ // then also add the touched wallpaper windows so they are locked in for the
+ // duration of the touch gesture. We do not collect wallpapers during HOVER_MOVE or
+ // SCROLL because the wallpaper engine only supports touch events. We would need to
+ // add a mechanism similar to View.onGenericMotionEvent to enable wallpapers to
+ // handle these events.
+ if (isDownOrPointerDown && targetFlags.test(InputTarget::Flags::FOREGROUND) &&
windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle);
@@ -2488,8 +2451,10 @@
/* 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() && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
- LOG(INFO) << "Dropping event because the pointer is not down or we previously "
+ if (!tempTouchState.isDown(entry.deviceId) &&
+ maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId
+ << " is not down or we previously "
"dropped the pointer down event in display "
<< displayId << ": " << entry.getDescription();
outInjectionResult = InputEventInjectionResult::FAILED;
@@ -2538,7 +2503,7 @@
if (newTouchedWindowHandle != nullptr &&
!haveSameToken(oldTouchedWindowHandle, newTouchedWindowHandle)) {
- ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
+ ALOGI("Touch is slipping out of window %s into window %s in display %" PRId32,
oldTouchedWindowHandle->getName().c_str(),
newTouchedWindowHandle->getName().c_str(), displayId);
@@ -2549,9 +2514,11 @@
const TouchedWindow& touchedWindow =
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
- addWindowTargetLocked(oldTouchedWindowHandle,
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds,
- touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
+ addPointerWindowTargetLocked(oldTouchedWindowHandle,
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
+ pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ targets);
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2664,16 +2631,14 @@
// Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (!touchedWindow.hasTouchingPointers(entry.deviceId) &&
- !touchedWindow.hasHoveringPointers(entry.deviceId)) {
- // Windows with hovering pointers are getting persisted inside TouchState.
- // Do not send this event to those windows.
+ std::bitset<MAX_POINTER_ID + 1> touchingPointers =
+ touchedWindow.getTouchingPointers(entry.deviceId);
+ if (touchingPointers.none()) {
continue;
}
-
- addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.getTouchingPointers(entry.deviceId),
- touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
+ addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+ touchingPointers,
+ touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
}
// During targeted injection, only allow owned targets to receive events
@@ -2706,37 +2671,30 @@
}
outInjectionResult = InputEventInjectionResult::SUCCEEDED;
- // Drop the outside or hover touch windows since we will not care about them
- // in the next iteration.
- tempTouchState.filterNonAsIsTouchWindows();
- // Update final pieces of touch state if the injector had permission.
- if (switchedDevice) {
- if (DEBUG_FOCUS) {
- ALOGD("Conflicting pointer actions: Switched to a different device.");
+ for (TouchedWindow& touchedWindow : tempTouchState.windows) {
+ // Targets that we entered in a slippery way will now become AS-IS targets
+ if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
+ touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER);
+ touchedWindow.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS;
}
- *outConflictingPointerActions = true;
}
+ // Update final pieces of touch state if the injector had permission.
if (isHoverAction) {
- // Started hovering, therefore no longer down.
- if (oldState && oldState->isDown()) {
- ALOGD_IF(DEBUG_FOCUS,
- "Conflicting pointer actions: Hover received while pointer was down.");
- *outConflictingPointerActions = true;
+ if (oldState && oldState->isDown(entry.deviceId)) {
+ // Started hovering, but the device is already down: reject the hover event
+ LOG(ERROR) << "Got hover event " << entry.getDescription()
+ << " but the device is already down " << oldState->dump();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
}
} else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
// Pointer went up.
tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id);
} else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
- tempTouchState.reset();
- } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
- // First pointer went down.
- if (oldState && (oldState->isDown() || oldState->hasHoveringPointers())) {
- ALOGD("Conflicting pointer actions: Down received while already down or hovering.");
- *outConflictingPointerActions = true;
- }
+ tempTouchState.removeAllPointersForDevice(entry.deviceId);
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
// One pointer went up.
const int32_t pointerIndex = MotionEvent::getActionIndex(action);
@@ -2885,7 +2843,6 @@
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const {
std::vector<InputTarget>::iterator it =
@@ -2907,8 +2864,54 @@
it = inputTargets.end() - 1;
}
- ALOG_ASSERT(it->flags == targetFlags);
- ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
+ LOG_ALWAYS_FATAL_IF(it->flags != targetFlags);
+ LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
+}
+
+void InputDispatcher::addPointerWindowTargetLocked(
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const
+ REQUIRES(mLock) {
+ if (pointerIds.none()) {
+ for (const auto& target : inputTargets) {
+ LOG(INFO) << "Target: " << target;
+ }
+ LOG(FATAL) << "No pointers specified for " << windowHandle->getName();
+ return;
+ }
+ std::vector<InputTarget>::iterator it =
+ std::find_if(inputTargets.begin(), inputTargets.end(),
+ [&windowHandle](const InputTarget& inputTarget) {
+ return inputTarget.inputChannel->getConnectionToken() ==
+ windowHandle->getToken();
+ });
+
+ // This is a hack, because the actual entry could potentially be an ACTION_DOWN event that
+ // causes a HOVER_EXIT to be generated. That means that the same entry of ACTION_DOWN would
+ // have DISPATCH_AS_HOVER_EXIT and DISPATCH_AS_IS. And therefore, we have to create separate
+ // input targets for hovering pointers and for touching pointers.
+ // If we picked an existing input target above, but it's for HOVER_EXIT - let's use a new
+ // target instead.
+ if (it != inputTargets.end() && it->flags.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) {
+ // Force the code below to create a new input target
+ it = inputTargets.end();
+ }
+
+ const WindowInfo* windowInfo = windowHandle->getInfo();
+
+ if (it == inputTargets.end()) {
+ std::optional<InputTarget> target =
+ createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget);
+ if (!target) {
+ return;
+ }
+ inputTargets.push_back(*target);
+ it = inputTargets.end() - 1;
+ }
+
+ LOG_ALWAYS_FATAL_IF(it->flags != targetFlags);
+ LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
it->addPointers(pointerIds, windowInfo->transform);
}
@@ -3366,6 +3369,27 @@
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
}
+ // Check if we need to cancel any of the ongoing gestures. We don't support multiple
+ // devices being active at the same time in the same window, so if a new device is
+ // active, cancel the gesture from the old device.
+
+ std::unique_ptr<EventEntry> cancelEvent =
+ connection->inputState
+ .cancelConflictingInputStream(motionEntry,
+ dispatchEntry->resolvedAction);
+ if (cancelEvent != nullptr) {
+ LOG(INFO) << "Canceling pointers for device " << motionEntry.deviceId << " in "
+ << connection->getInputChannelName() << " with event "
+ << cancelEvent->getDescription();
+ std::unique_ptr<DispatchEntry> cancelDispatchEntry =
+ createDispatchEntry(inputTarget, std::move(cancelEvent),
+ InputTarget::Flags::DISPATCH_AS_IS);
+
+ // Send these cancel events to the queue before sending the event from the new
+ // device.
+ connection->outboundQueue.emplace_back(std::move(cancelDispatchEntry));
+ }
+
if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
LOG(WARNING) << "channel " << connection->getInputChannelName()
@@ -3975,7 +3999,7 @@
: std::nullopt;
if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) {
addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS,
- /*pointerIds=*/{}, keyEntry.downTime, targets);
+ keyEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
}
@@ -3994,8 +4018,8 @@
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, pointerIds,
- motionEntry.downTime, targets);
+ addPointerWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS,
+ pointerIds, motionEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
const auto it = mDisplayInfos.find(motionEntry.displayId);
@@ -4069,8 +4093,8 @@
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- addWindowTargetLocked(windowHandle, targetFlags, pointerIds,
- motionEntry.downTime, targets);
+ addPointerWindowTargetLocked(windowHandle, targetFlags, pointerIds,
+ motionEntry.downTime, targets);
} else {
targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel,
.flags = targetFlags});
@@ -5139,10 +5163,8 @@
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Touched window was removed: %s in display %" PRId32,
- touchedWindow.windowHandle->getName().c_str(), displayId);
- }
+ LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
+ << " in display %" << displayId;
std::shared_ptr<InputChannel> touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
@@ -5973,9 +5995,8 @@
return BAD_VALUE;
}
std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds();
- if (deviceIds.size() != 1) {
- LOG(WARNING) << "Can't pilfer. Currently touching devices: " << dumpSet(deviceIds)
- << " in window: " << windowPtr->dump();
+ if (deviceIds.empty()) {
+ LOG(WARNING) << "Can't pilfer: no touching devices in window: " << windowPtr->dump();
return BAD_VALUE;
}
@@ -6818,10 +6839,11 @@
if (oldWallpaper != nullptr) {
const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
- addWindowTargetLocked(oldWallpaper,
- oldTouchedWindow.targetFlags |
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
- pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId), targets);
+ addPointerWindowTargetLocked(oldWallpaper,
+ oldTouchedWindow.targetFlags |
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
+ pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId),
+ targets);
state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper);
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 0020301..a1127a0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -524,7 +524,7 @@
nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
std::vector<InputTarget> findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
+ nsecs_t currentTime, const MotionEntry& entry,
android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
std::vector<Monitor> selectResponsiveMonitorsLocked(
const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
@@ -535,9 +535,13 @@
std::optional<nsecs_t> firstDownTimeInTarget) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
+ void addPointerWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ std::optional<nsecs_t> firstDownTimeInTarget,
+ std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index b348808..09b5186 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -24,6 +24,19 @@
namespace android::inputdispatcher {
+namespace {
+bool isHoverAction(int32_t action) {
+ switch (MotionEvent::getActionMasked(action)) {
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_HOVER_EXIT: {
+ return true;
+ }
+ }
+ return false;
+}
+} // namespace
+
InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}
InputState::~InputState() {}
@@ -89,6 +102,28 @@
* false if the incoming event should be dropped.
*/
bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
+ // Don't track non-pointer events
+ if (!isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ // This is a focus-dispatched event; we don't track its state.
+ return true;
+ }
+
+ if (!mMotionMementos.empty()) {
+ const MotionMemento& lastMemento = mMotionMementos.back();
+ if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) &&
+ !isStylusEvent(entry.source, entry.pointerProperties)) {
+ // We already have a stylus stream, and the new event is not from stylus.
+ if (!lastMemento.hovering) {
+ // If stylus is currently down, reject the new event unconditionally.
+ return false;
+ }
+ }
+ if (!lastMemento.hovering && isHoverAction(action)) {
+ // Reject hovers if already down
+ return false;
+ }
+ }
+
int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
switch (actionMasked) {
case AMOTION_EVENT_ACTION_UP:
@@ -266,6 +301,136 @@
return pointerProperties.size();
}
+bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry,
+ int32_t resolvedAction) const {
+ if (!isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ // This is a focus-dispatched event that should not affect the previous stream.
+ return false;
+ }
+
+ // New MotionEntry pointer event is coming in.
+
+ // If this is a new gesture, and it's from a different device, then, in general, we will cancel
+ // the current gesture.
+ // However, because stylus should be preferred over touch, we need to treat some cases in a
+ // special way.
+ if (mMotionMementos.empty()) {
+ // There is no ongoing pointer gesture, so there is nothing to cancel
+ return false;
+ }
+
+ const MotionMemento& lastMemento = mMotionMementos.back();
+ const int32_t actionMasked = MotionEvent::getActionMasked(resolvedAction);
+
+ // For compatibility, only one input device can be active at a time in the same window.
+ if (lastMemento.deviceId == motionEntry.deviceId) {
+ // In general, the same device should produce self-consistent streams so nothing needs to
+ // be canceled. But there is one exception:
+ // Sometimes ACTION_DOWN is received without a corresponding HOVER_EXIT. To account for
+ // that, cancel the previous hovering stream
+ if (actionMasked == AMOTION_EVENT_ACTION_DOWN && lastMemento.hovering) {
+ return true;
+ }
+
+ // Use the previous stream cancellation logic to generate all HOVER_EXIT events.
+ // If this hover event was generated as a result of the pointer leaving the window,
+ // the HOVER_EXIT event should have the same coordinates as the previous
+ // HOVER_MOVE event in this stream. Ensure that all HOVER_EXITs have the same
+ // coordinates as the previous event by cancelling the stream here. With this approach, the
+ // HOVER_EXIT event is generated from the previous event.
+ if (actionMasked == AMOTION_EVENT_ACTION_HOVER_EXIT && lastMemento.hovering) {
+ return true;
+ }
+
+ // If the stream changes its source, just cancel the current gesture to be safe. It's
+ // possible that the app isn't handling source changes properly
+ if (motionEntry.source != lastMemento.source) {
+ LOG(INFO) << "Canceling stream: last source was "
+ << inputEventSourceToString(lastMemento.source) << " and new event is "
+ << motionEntry;
+ return true;
+ }
+
+ // If the injection is happening into two different displays, the same injected device id
+ // could be going into both. And at this time, if mirroring is active, the same connection
+ // would receive different events from each display. Since the TouchStates are per-display,
+ // it's unlikely that those two streams would be consistent with each other. Therefore,
+ // cancel the previous gesture if the display id changes.
+ if (motionEntry.displayId != lastMemento.displayId) {
+ LOG(INFO) << "Canceling stream: last displayId was "
+ << inputEventSourceToString(lastMemento.displayId) << " and new event is "
+ << motionEntry;
+ return true;
+ }
+
+ return false;
+ }
+
+ // We want stylus down to block touch and other source types, but stylus hover should not
+ // have such an effect.
+ if (isHoverAction(motionEntry.action) && !lastMemento.hovering) {
+ // New event is a hover. Keep the current non-hovering gesture instead
+ return false;
+ }
+
+ if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) && !lastMemento.hovering) {
+ // We have non-hovering stylus already active.
+ if (isStylusEvent(motionEntry.source, motionEntry.pointerProperties) &&
+ actionMasked == AMOTION_EVENT_ACTION_DOWN) {
+ // If this new event is a stylus from a different device going down, then cancel the old
+ // stylus and allow the new stylus to take over
+ return true;
+ }
+
+ // Keep the current stylus gesture.
+ return false;
+ }
+
+ // Cancel the current gesture if this is a start of a new gesture from a new device.
+ if (actionMasked == AMOTION_EVENT_ACTION_DOWN ||
+ actionMasked == AMOTION_EVENT_ACTION_HOVER_ENTER) {
+ return true;
+ }
+ // By default, don't cancel any events.
+ return false;
+}
+
+std::unique_ptr<EventEntry> InputState::cancelConflictingInputStream(const MotionEntry& motionEntry,
+ int32_t resolvedAction) {
+ if (!shouldCancelPreviousStream(motionEntry, resolvedAction)) {
+ return {};
+ }
+
+ const MotionMemento& memento = mMotionMementos.back();
+
+ // Cancel the last device stream
+ std::unique_ptr<MotionEntry> cancelEntry =
+ createCancelEntryForMemento(memento, motionEntry.eventTime);
+
+ if (!trackMotion(*cancelEntry, cancelEntry->action, cancelEntry->flags)) {
+ LOG(FATAL) << "Generated inconsistent cancel event!";
+ }
+ return cancelEntry;
+}
+
+std::unique_ptr<MotionEntry> InputState::createCancelEntryForMemento(const MotionMemento& memento,
+ nsecs_t eventTime) const {
+ const int32_t action =
+ memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
+ int32_t flags = memento.flags;
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ return std::make_unique<MotionEntry>(mIdGenerator.nextId(), eventTime, memento.deviceId,
+ memento.source, memento.displayId, memento.policyFlags,
+ action, /*actionButton=*/0, flags, AMETA_NONE,
+ /*buttonState=*/0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime,
+ memento.pointerProperties, memento.pointerCoords);
+}
+
std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents(
nsecs_t currentTime, const CancelationOptions& options) {
std::vector<std::unique_ptr<EventEntry>> events;
@@ -284,24 +449,7 @@
for (const MotionMemento& memento : mMotionMementos) {
if (shouldCancelMotion(memento, options)) {
if (options.pointerIds == std::nullopt) {
- const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
- : AMOTION_EVENT_ACTION_CANCEL;
- int32_t flags = memento.flags;
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- flags |= AMOTION_EVENT_FLAG_CANCELED;
- }
- events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
- memento.deviceId, memento.source,
- memento.displayId, memento.policyFlags,
- action, /*actionButton=*/0, flags, AMETA_NONE,
- /*buttonState=*/0, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- memento.xPrecision, memento.yPrecision,
- memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime,
- memento.pointerProperties,
- memento.pointerCoords));
+ events.push_back(createCancelEntryForMemento(memento, currentTime));
} else {
std::vector<std::unique_ptr<MotionEntry>> pointerCancelEvents =
synthesizeCancelationEventsForPointers(memento, options.pointerIds.value(),
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 3adbba0..686c432 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -48,6 +48,10 @@
// and should be skipped.
bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
+ // Create cancel events for the previous stream if the current motionEntry requires it.
+ std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry,
+ int32_t resolvedAction);
+
// Synthesizes cancelation events for the current state and resets the tracked state.
std::vector<std::unique_ptr<EventEntry>> synthesizeCancelationEvents(
nsecs_t currentTime, const CancelationOptions& options);
@@ -123,6 +127,9 @@
static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
+ bool shouldCancelPreviousStream(const MotionEntry& motionEntry, int32_t resolvedAction) const;
+ std::unique_ptr<MotionEntry> createCancelEntryForMemento(const MotionMemento& memento,
+ nsecs_t eventTime) const;
// Synthesizes pointer cancel events for a particular set of pointers.
std::vector<std::unique_ptr<MotionEntry>> synthesizeCancelationEventsForPointers(
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 9dcf615..2ead171 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <gui/WindowInfo.h>
@@ -31,15 +32,6 @@
*this = TouchState();
}
-std::set<int32_t> TouchState::getActiveDeviceIds() const {
- std::set<int32_t> out;
- for (const TouchedWindow& w : windows) {
- std::set<int32_t> deviceIds = w.getActiveDeviceIds();
- out.insert(deviceIds.begin(), deviceIds.end());
- }
- return out;
-}
-
bool TouchState::hasTouchingPointers(DeviceId deviceId) const {
return std::any_of(windows.begin(), windows.end(), [&](const TouchedWindow& window) {
return window.hasTouchingPointers(deviceId);
@@ -65,9 +57,9 @@
}
}
-void TouchState::clearHoveringPointers() {
+void TouchState::clearHoveringPointers(DeviceId deviceId) {
for (TouchedWindow& touchedWindow : windows) {
- touchedWindow.clearHoveringPointers();
+ touchedWindow.removeAllHoveringPointersForDevice(deviceId);
}
clearWindowsWithoutPointers();
}
@@ -82,9 +74,16 @@
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
std::optional<nsecs_t> firstDownTimeInTarget) {
+ if (touchingPointerIds.none()) {
+ LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
+ return;
+ }
for (TouchedWindow& touchedWindow : windows) {
// We do not compare windows by token here because two windows that share the same token
- // may have a different transform
+ // may have a different transform. They will be combined later when we create InputTargets.
+ // At that point, per-pointer window transform will be considered.
+ // An alternative design choice here would have been to compare here by token, but to
+ // store per-pointer transform.
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
@@ -237,14 +236,16 @@
return *it;
}
-bool TouchState::isDown() const {
- return std::any_of(windows.begin(), windows.end(),
- [](const TouchedWindow& window) { return window.hasTouchingPointers(); });
+bool TouchState::isDown(DeviceId deviceId) const {
+ return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
+ return window.hasTouchingPointers(deviceId);
+ });
}
-bool TouchState::hasHoveringPointers() const {
- return std::any_of(windows.begin(), windows.end(),
- [](const TouchedWindow& window) { return window.hasHoveringPointers(); });
+bool TouchState::hasHoveringPointers(DeviceId deviceId) const {
+ return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
+ return window.hasHoveringPointers(deviceId);
+ });
}
std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index f016936..e79c73b 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -39,8 +39,6 @@
void reset();
void clearWindowsWithoutPointers();
- std::set<DeviceId> getActiveDeviceIds() const;
-
bool hasTouchingPointers(DeviceId deviceId) const;
void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
void removeTouchingPointerFromWindow(DeviceId deviceId, int32_t pointerId,
@@ -52,7 +50,7 @@
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, int32_t hoveringPointerId);
void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId);
- void clearHoveringPointers();
+ void clearHoveringPointers(DeviceId deviceId);
void removeAllPointersForDevice(DeviceId deviceId);
void removeWindowByToken(const sp<IBinder>& token);
@@ -72,8 +70,8 @@
const TouchedWindow& getTouchedWindow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const;
// Whether any of the windows are currently being touched
- bool isDown() const;
- bool hasHoveringPointers() const;
+ bool isDown(DeviceId deviceId) const;
+ bool hasHoveringPointers(DeviceId deviceId) const;
std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
DeviceId deviceId, int32_t pointerId) const;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index ff4b425..5367751 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -45,12 +45,16 @@
return state.hoveringPointerIds.any();
}
-void TouchedWindow::clearHoveringPointers() {
- for (auto& [_, state] : mDeviceStates) {
- state.hoveringPointerIds.reset();
+void TouchedWindow::clearHoveringPointers(DeviceId deviceId) {
+ auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return;
}
-
- std::erase_if(mDeviceStates, [](const auto& pair) { return !pair.second.hasPointers(); });
+ DeviceState& state = stateIt->second;
+ state.hoveringPointerIds.reset();
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
+ }
}
bool TouchedWindow::hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const {
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 3f760c0..6d2283e 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -71,7 +71,7 @@
void removeAllTouchingPointersForDevice(DeviceId deviceId);
void removeAllHoveringPointersForDevice(DeviceId deviceId);
- void clearHoveringPointers();
+ void clearHoveringPointers(DeviceId deviceId);
std::string dump() const;
private:
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 34323c3..3c87f71 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2409,9 +2409,10 @@
using InputDispatcherMultiDeviceTest = InputDispatcherTest;
/**
- * One window. Stylus down on the window. Next, touch from another device goes down.
+ * One window. Stylus down on the window. Next, touch from another device goes down. Ensure that
+ * touch is dropped, because stylus should be preferred over touch.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusDownAndTouchDown) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownBlocksTouchDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2434,34 +2435,31 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
.build());
- // Touch cancels stylus
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId),
- WithCoords(100, 110)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId),
- WithCoords(140, 145)));
// Touch move
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
.build());
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
- WithCoords(141, 146)));
+ // Touch is ignored because stylus is already down
- // Subsequent stylus movements are dropped
+ // Subsequent stylus movements are delivered correctly
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
.build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+
window->assertNoEvents();
}
/**
* One window and one spy window. Stylus down on the window. Next, touch from another device goes
- * down.
+ * down. Ensure that touch is dropped, because stylus should be preferred over touch.
* Similar test as above, but with added SPY window.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyAndTouchDown) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyBlocksTouchDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2497,30 +2495,28 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
.build());
- window->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
- // Subsequent stylus movements are dropped
+
+ // Touch is ignored because stylus is already down
+
+ // Subsequent stylus movements are delivered correctly
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
.build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
window->assertNoEvents();
spyWindow->assertNoEvents();
}
/**
- * One window. Stylus hover on the window. Next, touch from another device goes down.
+ * One window. Stylus hover on the window. Next, touch from another device goes down. Ensure that
+ * touch is not dropped, because stylus hover should be ignored.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusHoverAndTouchDown) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2548,19 +2544,174 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
.build());
- window->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus hover is canceled because touch is down
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT),
+ WithDeviceId(stylusDeviceId), WithCoords(100, 110)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId),
+ WithCoords(140, 145)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(141, 146)));
+
// Subsequent stylus movements are ignored
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
.build());
+
+ // but subsequent touches continue to be delivered
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(142, 147)));
+}
+
+/**
+ * One window. Touch down on the window. Then, stylus hover on the window from another device.
+ * Ensure that touch is not canceled, because stylus hover should be dropped.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) {
+ 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));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus hover on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ // Stylus hover movement is dropped
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
+ .build());
+ // Subsequent touch movements are delivered correctly
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(142, 147)));
+}
+
+/**
+ * One window. Stylus down on the window. Then, stylus from another device goes down. Ensure that
+ * the latest stylus takes over. That is, old stylus should be canceled and the new stylus should
+ * become active.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, LatestStylusWins) {
+ 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));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t stylusDeviceId1 = 3;
+ constexpr int32_t stylusDeviceId2 = 5;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(99).y(100))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(101))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId1)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId1)));
+
+ // Second stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId2)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(9).y(10))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId2)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(10).y(11))
+ .build());
+
+ // First stylus is canceled, second one takes over.
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId1)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId2)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId2)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102))
+ .build());
+ // Subsequent stylus movements are delivered correctly
window->assertNoEvents();
}
/**
+ * One window. Touch down on the window. Then, stylus down on the window from another device.
+ * Ensure that is canceled, because stylus down should be preferred over touch.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, TouchIsCanceledByStylusDown) {
+ 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));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus down on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Subsequent stylus movements are delivered correctly
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+}
+
+/**
* 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.
@@ -2617,8 +2768,8 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(mouseDeviceId)));
+ leftWindow->assertNoEvents();
+
rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
// Second touch pointer down on left window
@@ -2627,6 +2778,11 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
.build());
+ // Since this is now a new splittable pointer going down on the left window, and it's coming
+ // from a different device, the current gesture in the left window (pointer down) should first
+ // be canceled.
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(mouseDeviceId)));
leftWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
// This MOVE event is not necessary (doesn't carry any new information), but it's there in the
@@ -2672,8 +2828,6 @@
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
@@ -2683,18 +2837,14 @@
.pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(120))
.build());
leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
- rightWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(mouseDeviceId)));
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(310).y(110))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
rightWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
leftWindow->assertNoEvents();
rightWindow->assertNoEvents();
@@ -2745,29 +2895,30 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ leftWindow->assertNoEvents();
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Stylus movements continue, but are ignored because the touch went down more recently.
+ // Spy window does not receive touch events, because stylus events take precedence, and it
+ // already has an active stylus gesture.
+
+ // Stylus movements continue. They should be delivered to the left window and to the spy window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
.build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+ // Further MOVE events keep going to the right window only
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(310).y(110))
.build());
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
- spyWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
spyWindow->assertNoEvents();
leftWindow->assertNoEvents();
@@ -2779,8 +2930,10 @@
* both.
* Check hover in left window and touch down in the right window.
* At first, spy should receive hover, but the touch down should cancel hovering inside spy.
+ * At the same time, left and right should be getting independent streams of hovering and touch,
+ * respectively.
*/
-TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverAndTouchWithSpy) {
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlockedByTouchWithSpy) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow =
@@ -2813,13 +2966,13 @@
spyWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
- // Touch down on the right window.
+ // Touch down on the right window. Spy doesn't receive this touch because it already has
+ // stylus hovering there.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ leftWindow->assertNoEvents();
spyWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
spyWindow->consumeMotionEvent(
@@ -2827,11 +2980,13 @@
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Stylus movements continue, but are ignored because the touch is down.
+ // Stylus movements continue. They should be delivered to the left window only.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
.build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
// Touch movements continue. They should be delivered to the right window and to the spy
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
@@ -3054,7 +3209,7 @@
* While the touch is down, new hover events from the stylus device should be ignored. After the
* touch is gone, stylus hovering should start working again.
*/
-TEST_F(InputDispatcherMultiDeviceTest, StylusHoverAndTouchTap) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -3080,20 +3235,20 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
+ // The touch device should cause hover to stop!
window->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Continue hovering with stylus. Injection will fail because touch is already down.
- ASSERT_EQ(InputEventInjectionResult::FAILED,
+ // Continue hovering with stylus.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(60).y(60))
.build()));
- // No event should be sent. This event should be ignored because a pointer from another device
- // is already down.
+ // Hovers are now ignored
// Lift up the finger
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -3105,7 +3260,6 @@
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(touchDeviceId)));
- // Now that the touch is gone, stylus hovering should start working again
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
@@ -7822,10 +7976,23 @@
// should be ANR'd first.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- FOCUSED_WINDOW_LOCATION))
- << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER)
+ .x(FOCUSED_WINDOW_LOCATION.x)
+ .y(FOCUSED_WINDOW_LOCATION.y))
+ .build()));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER)
+ .x(FOCUSED_WINDOW_LOCATION.x)
+ .y(FOCUSED_WINDOW_LOCATION.y))
+ .build()));
mFocusedWindow->consumeMotionDown();
+ mFocusedWindow->consumeMotionUp();
mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
ADISPLAY_ID_DEFAULT, /*flags=*/0);
// We consumed all events, so no ANR
@@ -7833,17 +8000,20 @@
mFakePolicy->assertNotifyAnrWasNotCalled();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- FOCUSED_WINDOW_LOCATION));
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER)
+ .x(FOCUSED_WINDOW_LOCATION.x)
+ .y(FOCUSED_WINDOW_LOCATION.y))
+ .build()));
std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(unfocusedSequenceNum);
const std::chrono::duration timeout =
mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow);
- // Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
- // sequence to make it consistent
- mFocusedWindow->consumeMotionCancel();
+
mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
mFocusedWindow->consumeMotionDown();
// This cancel is generated because the connection was unresponsive
@@ -10356,13 +10526,12 @@
.build());
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- leftWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
- spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spy->assertNoEvents();
// Act: pilfer from spy. Spy is currently receiving touch events.
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
rightWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
@@ -10376,7 +10545,7 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(52))
.build());
- spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
spy->assertNoEvents();
leftWindow->assertNoEvents();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index ce845d9..0e49b75 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -209,7 +209,7 @@
(barrierFrameNumber > bufferData->frameNumber))) {
ALOGE("Out of order buffers detected for %s producedId=%d frameNumber=%" PRIu64
" -> producedId=%d frameNumber=%" PRIu64,
- getDebugString().c_str(), bufferData->producerId, bufferData->frameNumber,
+ getDebugString().c_str(), barrierProducerId, barrierFrameNumber,
bufferData->producerId, frameNumber);
TransactionTraceWriter::getInstance().invoke("out_of_order_buffers_",
/*overwrite=*/false);
@@ -401,6 +401,7 @@
if (!handleAlive) debug << " !handle";
if (z != 0) debug << " z=" << z;
if (layerStack.id != 0) debug << " layerStack=" << layerStack.id;
+ debug << "}";
return debug.str();
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 73b1ca8..b742394 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2932,11 +2932,6 @@
}
}
- // Prevent tracing the same release multiple times.
- if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
- mPreviousReleasedFrameNumber = mPreviousFrameNumber;
- }
-
if (ch != nullptr) {
ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f67da2a..26e4666 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1235,9 +1235,6 @@
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
sp<IBinder> mPreviousReleaseBufferEndpoint;
- uint64_t mPreviousReleasedFrameNumber = 0;
-
- uint64_t mPreviousBarrierFrameNumber = 0;
bool mReleasePreviousBuffer = false;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e6d6866..bc6d375 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1232,7 +1232,7 @@
}
status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken,
- DisplayModeId modeId) {
+ DisplayModeId modeId, Fps minFps, Fps maxFps) {
ATRACE_CALL();
if (!displayToken) {
@@ -1265,13 +1265,15 @@
}
const Fps fps = *fpsOpt;
+ const FpsRange physical = {fps, fps};
+ const FpsRange render = {minFps.isValid() ? minFps : fps, maxFps.isValid() ? maxFps : fps};
+ const FpsRanges ranges = {physical, render};
// Keep the old switching type.
const bool allowGroupSwitching =
display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching;
- const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId,
- {fps, fps},
+ const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId, ranges, ranges,
allowGroupSwitching};
return setDesiredDisplayModeSpecsInternal(display, policy);
@@ -6932,6 +6934,12 @@
return NO_ERROR;
}
case 1035: {
+ // Parameters:
+ // - (required) i32 mode id.
+ // - (optional) i64 display id. Using default display if not provided.
+ // - (optional) f min render rate. Using mode's fps is not provided.
+ // - (optional) f max render rate. Using mode's fps is not provided.
+
const int modeId = data.readInt32();
const auto display = [&]() -> sp<IBinder> {
@@ -6948,8 +6956,21 @@
return nullptr;
}();
+ const auto getFps = [&] {
+ float value;
+ if (data.readFloat(&value) == NO_ERROR) {
+ return Fps::fromValue(value);
+ }
+
+ return Fps();
+ };
+
+ const auto minFps = getFps();
+ const auto maxFps = getFps();
+
mDebugDisplayModeSetByBackdoor = false;
- const status_t result = setActiveModeFromBackdoor(display, DisplayModeId{modeId});
+ const status_t result =
+ setActiveModeFromBackdoor(display, DisplayModeId{modeId}, minFps, maxFps);
mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
return result;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7500b96..96b67b8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -684,7 +684,8 @@
void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
REQUIRES(mStateLock);
- status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
+ status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
+ Fps maxFps);
void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);