Merge "Use std::unique_ptr for DispatchEntry" into main
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 4081514..a401838 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
@@ -117,10 +118,26 @@
});
if (!found) {
+ std::set<std::string> instances;
+ forEachManifest([&](const ManifestWithDescription& mwd) {
+ std::set<std::string> res = mwd.manifest->getAidlInstances(aname.package, aname.iface);
+ instances.insert(res.begin(), res.end());
+ return true;
+ });
+
+ std::string available;
+ if (instances.empty()) {
+ available = "No alternative instances declared in VINTF";
+ } else {
+ // for logging only. We can't return this information to the client
+ // because they may not have permissions to find or list those
+ // instances
+ available = "VINTF declared instances: " + base::Join(instances, ", ");
+ }
// Although it is tested, explicitly rebuilding qualified name, in case it
// becomes something unexpected.
- ALOGI("Could not find %s.%s/%s in the VINTF manifest.", aname.package.c_str(),
- aname.iface.c_str(), aname.instance.c_str());
+ ALOGI("Could not find %s.%s/%s in the VINTF manifest. %s.", aname.package.c_str(),
+ aname.iface.c_str(), aname.instance.c_str(), available.c_str());
}
return found;
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
index b857482..14dd463 100644
--- a/include/input/InputVerifier.h
+++ b/include/input/InputVerifier.h
@@ -46,7 +46,7 @@
public:
InputVerifier(const std::string& name);
- android::base::Result<void> processMovement(int32_t deviceId, int32_t action,
+ android::base::Result<void> processMovement(int32_t deviceId, int32_t source, int32_t action,
uint32_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, int32_t flags);
diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
index e4dbb2d..7390d49 100644
--- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
@@ -29,40 +29,65 @@
const char* fdType;
std::vector<unique_fd> fds = provider->PickValueInArray<
- std::function<std::vector<unique_fd>()>>({
- [&]() {
- fdType = "ashmem";
- std::vector<unique_fd> ret;
- ret.push_back(unique_fd(
- ashmem_create_region("binder test region",
- provider->ConsumeIntegralInRange<size_t>(0, 4096))));
- return ret;
- },
- [&]() {
- fdType = "/dev/null";
- std::vector<unique_fd> ret;
- ret.push_back(unique_fd(open("/dev/null", O_RDWR)));
- return ret;
- },
- [&]() {
- fdType = "pipefd";
+ std::function<std::vector<unique_fd>()>>(
+ {[&]() {
+ fdType = "ashmem";
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(
+ ashmem_create_region("binder test region",
+ provider->ConsumeIntegralInRange<size_t>(0, 4096))));
+ return ret;
+ },
+ [&]() {
+ fdType = "/dev/null";
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(open("/dev/null", O_RDWR)));
+ return ret;
+ },
+ [&]() {
+ fdType = "pipefd";
- int pipefds[2];
+ int pipefds[2];
- int flags = O_CLOEXEC;
- if (provider->ConsumeBool()) flags |= O_DIRECT;
- if (provider->ConsumeBool()) flags |= O_NONBLOCK;
+ int flags = O_CLOEXEC;
+ if (provider->ConsumeBool()) flags |= O_DIRECT;
+ if (provider->ConsumeBool()) flags |= O_NONBLOCK;
- CHECK_EQ(0, pipe2(pipefds, flags)) << flags;
+ CHECK_EQ(0, pipe2(pipefds, flags)) << flags;
- if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]);
+ if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]);
- std::vector<unique_fd> ret;
- ret.push_back(unique_fd(pipefds[0]));
- ret.push_back(unique_fd(pipefds[1]));
- return ret;
- },
- })();
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(pipefds[0]));
+ ret.push_back(unique_fd(pipefds[1]));
+ return ret;
+ },
+ [&]() {
+ fdType = "tempfd";
+ char name[PATH_MAX];
+#if defined(__ANDROID__)
+ snprintf(name, sizeof(name), "/data/local/tmp/android-tempfd-test-%d-XXXXXX",
+ getpid());
+#else
+ snprintf(name, sizeof(name), "/tmp/android-tempfd-test-%d-XXXXXX", getpid());
+#endif
+ int fd = mkstemp(name);
+ CHECK_NE(fd, -1) << "Failed to create file " << name << ", errno: " << errno;
+ unlink(name);
+ if (provider->ConsumeBool()) {
+ CHECK_NE(TEMP_FAILURE_RETRY(
+ ftruncate(fd,
+ provider->ConsumeIntegralInRange<size_t>(0, 4096))),
+ -1)
+ << "Failed to truncate file, errno: " << errno;
+ }
+
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(fd));
+ return ret;
+ }
+
+ })();
for (const auto& fd : fds) CHECK(fd.ok()) << fd.get() << " " << fdType;
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4842476..ed5d5c1 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -63,8 +63,7 @@
namespace {
static bool isVndkEnabled() {
#ifdef __BIONIC__
- // TODO(b/290159430) Use ro.vndk.version to check if VNDK is enabled instead
- static bool isVndkEnabled = !android::base::GetBoolProperty("ro.vndk.deprecate", false);
+ static bool isVndkEnabled = android::base::GetProperty("ro.vndk.version", "") != "";
return isVndkEnabled;
#endif
return false;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 9a27d23..f17a654 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -200,6 +200,7 @@
cc_aconfig_library {
name: "libguiflags",
+ host_supported: true,
vendor_available: true,
aconfig_declarations: "libgui_flags",
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 36a01d3..ab4af1a 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -90,6 +90,28 @@
"--allowlist-var=AMOTION_EVENT_ACTION_DOWN",
"--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT",
"--allowlist-var=MAX_POINTER_ID",
+ "--allowlist-var=AINPUT_SOURCE_CLASS_NONE",
+ "--allowlist-var=AINPUT_SOURCE_CLASS_BUTTON",
+ "--allowlist-var=AINPUT_SOURCE_CLASS_POINTER",
+ "--allowlist-var=AINPUT_SOURCE_CLASS_NAVIGATION",
+ "--allowlist-var=AINPUT_SOURCE_CLASS_POSITION",
+ "--allowlist-var=AINPUT_SOURCE_CLASS_JOYSTICK",
+ "--allowlist-var=AINPUT_SOURCE_UNKNOWN",
+ "--allowlist-var=AINPUT_SOURCE_KEYBOARD",
+ "--allowlist-var=AINPUT_SOURCE_DPAD",
+ "--allowlist-var=AINPUT_SOURCE_GAMEPAD",
+ "--allowlist-var=AINPUT_SOURCE_TOUCHSCREEN",
+ "--allowlist-var=AINPUT_SOURCE_MOUSE",
+ "--allowlist-var=AINPUT_SOURCE_STYLUS",
+ "--allowlist-var=AINPUT_SOURCE_BLUETOOTH_STYLUS",
+ "--allowlist-var=AINPUT_SOURCE_TRACKBALL",
+ "--allowlist-var=AINPUT_SOURCE_MOUSE_RELATIVE",
+ "--allowlist-var=AINPUT_SOURCE_TOUCHPAD",
+ "--allowlist-var=AINPUT_SOURCE_TOUCH_NAVIGATION",
+ "--allowlist-var=AINPUT_SOURCE_JOYSTICK",
+ "--allowlist-var=AINPUT_SOURCE_HDMI",
+ "--allowlist-var=AINPUT_SOURCE_SENSOR",
+ "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER",
],
static_libs: [
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 1600013..30cedb0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -632,8 +632,8 @@
MotionEvent::actionToString(action).c_str()));
if (verifyEvents()) {
Result<void> result =
- mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
- pointerCoords, flags);
+ mInputVerifier.processMovement(deviceId, source, action, pointerCount,
+ pointerProperties, pointerCoords, flags);
if (!result.ok()) {
LOG(FATAL) << "Bad stream: " << result.error();
}
@@ -1276,13 +1276,13 @@
PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
const PointerCoords& currentCoords = current->getPointerById(id);
resampledCoords = currentCoords;
+ resampledCoords.isResampled = true;
if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
const PointerCoords& otherCoords = other->getPointerById(id);
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
lerp(currentCoords.getX(), otherCoords.getX(), alpha));
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
lerp(currentCoords.getY(), otherCoords.getY(), alpha));
- resampledCoords.isResampled = true;
ALOGD_IF(debugResampling(),
"[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
"other (%0.3f, %0.3f), alpha %0.3f",
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index 6c602e0..cec2445 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -33,7 +33,7 @@
InputVerifier::InputVerifier(const std::string& name)
: mVerifier(android::input::verifier::create(rust::String::lossy(name))){};
-Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
+Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t source, int32_t action,
uint32_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, int32_t flags) {
@@ -43,8 +43,8 @@
}
rust::Slice<const RustPointerProperties> properties{rpp.data(), rpp.size()};
rust::String errorMessage =
- android::input::verifier::process_movement(*mVerifier, deviceId, action, properties,
- static_cast<uint32_t>(flags));
+ android::input::verifier::process_movement(*mVerifier, deviceId, source, action,
+ properties, static_cast<uint32_t>(flags));
if (errorMessage.empty()) {
return {};
} else {
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index a0563f9..e8575a6 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -27,3 +27,10 @@
description: "Set to true to enable timer support for the touchpad Gestures library"
bug: "297192727"
}
+
+flag {
+ name: "enable_multi_device_input"
+ namespace: "input"
+ description: "Set to true to enable multi-device input: touch and stylus can be active at the same time, but in different windows"
+ bug: "211379801"
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 9d3b386..9725b00 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -23,6 +23,54 @@
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct DeviceId(pub i32);
+#[repr(u32)]
+pub enum SourceClass {
+ None = input_bindgen::AINPUT_SOURCE_CLASS_NONE,
+ Button = input_bindgen::AINPUT_SOURCE_CLASS_BUTTON,
+ Pointer = input_bindgen::AINPUT_SOURCE_CLASS_POINTER,
+ Navigation = input_bindgen::AINPUT_SOURCE_CLASS_NAVIGATION,
+ Position = input_bindgen::AINPUT_SOURCE_CLASS_POSITION,
+ Joystick = input_bindgen::AINPUT_SOURCE_CLASS_JOYSTICK,
+}
+
+bitflags! {
+ /// Source of the input device or input events.
+ pub struct Source: u32 {
+ /// SOURCE_UNKNOWN
+ const Unknown = input_bindgen::AINPUT_SOURCE_UNKNOWN;
+ /// SOURCE_KEYBOARD
+ const Keyboard = input_bindgen::AINPUT_SOURCE_KEYBOARD;
+ /// SOURCE_DPAD
+ const Dpad = input_bindgen::AINPUT_SOURCE_DPAD;
+ /// SOURCE_GAMEPAD
+ const Gamepad = input_bindgen::AINPUT_SOURCE_GAMEPAD;
+ /// SOURCE_TOUCHSCREEN
+ const Touchscreen = input_bindgen::AINPUT_SOURCE_TOUCHSCREEN;
+ /// SOURCE_MOUSE
+ const Mouse = input_bindgen::AINPUT_SOURCE_MOUSE;
+ /// SOURCE_STYLUS
+ const Stylus = input_bindgen::AINPUT_SOURCE_STYLUS;
+ /// SOURCE_BLUETOOTH_STYLUS
+ const BluetoothStylus = input_bindgen::AINPUT_SOURCE_BLUETOOTH_STYLUS;
+ /// SOURCE_TRACKBALL
+ const Trackball = input_bindgen::AINPUT_SOURCE_TRACKBALL;
+ /// SOURCE_MOUSE_RELATIVE
+ const MouseRelative = input_bindgen::AINPUT_SOURCE_MOUSE_RELATIVE;
+ /// SOURCE_TOUCHPAD
+ const Touchpad = input_bindgen::AINPUT_SOURCE_TOUCHPAD;
+ /// SOURCE_TOUCH_NAVIGATION
+ const TouchNavigation = input_bindgen::AINPUT_SOURCE_TOUCH_NAVIGATION;
+ /// SOURCE_JOYSTICK
+ const Joystick = input_bindgen::AINPUT_SOURCE_JOYSTICK;
+ /// SOURCE_HDMI
+ const HDMI = input_bindgen::AINPUT_SOURCE_HDMI;
+ /// SOURCE_SENSOR
+ const Sensor = input_bindgen::AINPUT_SOURCE_SENSOR;
+ /// SOURCE_ROTARY_ENCODER
+ const RotaryEncoder = input_bindgen::AINPUT_SOURCE_ROTARY_ENCODER;
+ }
+}
+
/// A rust enum representation of a MotionEvent action.
#[repr(u32)]
pub enum MotionAction {
@@ -134,3 +182,11 @@
const NO_FOCUS_CHANGE = input_bindgen::AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
}
}
+
+impl Source {
+ /// Return true if this source is from the provided class
+ pub fn is_from_class(&self, source_class: SourceClass) -> bool {
+ let class_bits = source_class as u32;
+ self.bits() & class_bits == class_bits
+ }
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index 64c0466..2d94e70 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -17,7 +17,7 @@
//! Contains the InputVerifier, used to validate a stream of input events.
use crate::ffi::RustPointerProperties;
-use crate::input::{DeviceId, MotionAction, MotionFlags};
+use crate::input::{DeviceId, MotionAction, MotionFlags, Source, SourceClass};
use log::info;
use std::collections::HashMap;
use std::collections::HashSet;
@@ -51,10 +51,15 @@
pub fn process_movement(
&mut self,
device_id: DeviceId,
+ source: Source,
action: u32,
pointer_properties: &[RustPointerProperties],
flags: MotionFlags,
) -> Result<(), String> {
+ if !source.is_from_class(SourceClass::Pointer) {
+ // Skip non-pointer sources like MOUSE_RELATIVE for now
+ return Ok(());
+ }
if self.should_log {
info!(
"Processing {} for device {:?} ({} pointer{}) on {}",
@@ -68,6 +73,13 @@
match action.into() {
MotionAction::Down => {
+ if pointer_properties.len() != 1 {
+ return Err(format!(
+ "{}: Invalid DOWN event: there are {} pointers in the event",
+ self.name,
+ pointer_properties.len()
+ ));
+ }
let it = self
.touching_pointer_ids_by_device
.entry(device_id)
@@ -90,10 +102,19 @@
));
}
let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ if it.len() != pointer_properties.len() - 1 {
+ return Err(format!(
+ "{}: There are currently {} touching pointers, but the incoming \
+ POINTER_DOWN event has {}",
+ self.name,
+ it.len(),
+ pointer_properties.len()
+ ));
+ }
let pointer_id = pointer_properties[action_index].id;
if it.contains(&pointer_id) {
return Err(format!(
- "{}: Pointer with id={} not found in the properties",
+ "{}: Pointer with id={} already present found in the properties",
self.name, pointer_id
));
}
@@ -108,11 +129,10 @@
}
}
MotionAction::PointerUp { action_index } => {
- if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
return Err(format!(
- "{}: Received POINTER_UP but no pointers are currently down for device \
- {:?}",
- self.name, device_id
+ "{}: ACTION_POINTER_UP touching pointers don't match",
+ self.name
));
}
let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
@@ -120,6 +140,13 @@
it.remove(&pointer_id);
}
MotionAction::Up => {
+ if pointer_properties.len() != 1 {
+ return Err(format!(
+ "{}: Invalid UP event: there are {} pointers in the event",
+ self.name,
+ pointer_properties.len()
+ ));
+ }
if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
return Err(format!(
"{} Received ACTION_UP but no pointers are currently down for device {:?}",
@@ -246,6 +273,7 @@
use crate::DeviceId;
use crate::MotionFlags;
use crate::RustPointerProperties;
+ use crate::Source;
#[test]
fn single_pointer_stream() {
let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
@@ -253,6 +281,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_DOWN,
&pointer_properties,
MotionFlags::empty(),
@@ -261,6 +290,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_MOVE,
&pointer_properties,
MotionFlags::empty(),
@@ -269,6 +299,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_UP,
&pointer_properties,
MotionFlags::empty(),
@@ -283,6 +314,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_DOWN,
&pointer_properties,
MotionFlags::empty(),
@@ -291,6 +323,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_MOVE,
&pointer_properties,
MotionFlags::empty(),
@@ -299,6 +332,7 @@
assert!(verifier
.process_movement(
DeviceId(2),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_DOWN,
&pointer_properties,
MotionFlags::empty(),
@@ -307,6 +341,7 @@
assert!(verifier
.process_movement(
DeviceId(2),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_MOVE,
&pointer_properties,
MotionFlags::empty(),
@@ -315,6 +350,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_UP,
&pointer_properties,
MotionFlags::empty(),
@@ -329,6 +365,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_DOWN,
&pointer_properties,
MotionFlags::empty(),
@@ -337,6 +374,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
&pointer_properties,
MotionFlags::CANCELED,
@@ -351,6 +389,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_DOWN,
&pointer_properties,
MotionFlags::empty(),
@@ -359,6 +398,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
&pointer_properties,
MotionFlags::empty(), // forgot to set FLAG_CANCELED
@@ -373,6 +413,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_UP,
&pointer_properties,
MotionFlags::empty(),
@@ -387,6 +428,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
&pointer_properties,
MotionFlags::empty(),
@@ -396,6 +438,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
&pointer_properties,
MotionFlags::empty(),
@@ -405,6 +448,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
&pointer_properties,
MotionFlags::empty(),
@@ -414,6 +458,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
&pointer_properties,
MotionFlags::empty(),
@@ -428,6 +473,7 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
&pointer_properties,
MotionFlags::empty(),
@@ -437,10 +483,28 @@
assert!(verifier
.process_movement(
DeviceId(1),
+ Source::Touchscreen,
input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
&pointer_properties,
MotionFlags::empty(),
)
.is_err());
}
+
+ // Send a MOVE without a preceding DOWN event. This is OK because it's from source
+ // MOUSE_RELATIVE, which is used during pointer capture. The verifier should allow such event.
+ #[test]
+ fn relative_mouse_move() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(2),
+ Source::MouseRelative,
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ }
}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 1d3c434..01d9599 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -19,7 +19,7 @@
mod input;
mod input_verifier;
-pub use input::{DeviceId, MotionAction, MotionFlags};
+pub use input::{DeviceId, MotionAction, MotionFlags, Source};
pub use input_verifier::InputVerifier;
#[cxx::bridge(namespace = "android::input")]
@@ -51,6 +51,7 @@
fn process_movement(
verifier: &mut InputVerifier,
device_id: i32,
+ source: u32,
action: u32,
pointer_properties: &[RustPointerProperties],
flags: u32,
@@ -73,12 +74,14 @@
fn process_movement(
verifier: &mut InputVerifier,
device_id: i32,
+ source: u32,
action: u32,
pointer_properties: &[RustPointerProperties],
flags: u32,
) -> String {
let result = verifier.process_movement(
DeviceId(device_id),
+ Source::from_bits(source).unwrap(),
action,
pointer_properties,
MotionFlags::from_bits(flags).unwrap(),
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 3ecf8ee..06b841b 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -25,7 +25,22 @@
namespace android {
-constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION;
+namespace {
+
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+static constexpr int32_t POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_2_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+struct Pointer {
+ int32_t id;
+ float x;
+ float y;
+ bool isResampled = false;
+};
+
+} // namespace
class InputPublisherAndConsumerTest : public testing::Test {
protected:
@@ -46,12 +61,28 @@
mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
- void PublishAndConsumeKeyEvent();
- void PublishAndConsumeMotionEvent();
- void PublishAndConsumeFocusEvent();
- void PublishAndConsumeCaptureEvent();
- void PublishAndConsumeDragEvent();
- void PublishAndConsumeTouchModeEvent();
+ void publishAndConsumeKeyEvent();
+ void publishAndConsumeMotionStream();
+ void publishAndConsumeFocusEvent();
+ void publishAndConsumeCaptureEvent();
+ void publishAndConsumeDragEvent();
+ void publishAndConsumeTouchModeEvent();
+ void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime,
+ const std::vector<Pointer>& pointers);
+
+private:
+ // The sequence number to use when publishing the next event
+ uint32_t mSeq = 1;
+
+ void publishAndConsumeMotionEvent(
+ int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags,
+ int32_t metaState, int32_t buttonState, MotionClassification classification,
+ float xScale, float yScale, float xOffset, float yOffset, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition, float rawXScale,
+ float rawYScale, float rawXOffset, float rawYOffset, nsecs_t downTime,
+ nsecs_t eventTime, const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerCoords>& pointerCoords);
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -63,10 +94,10 @@
mConsumer->getChannel()->getConnectionToken());
}
-void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeKeyEvent() {
status_t status;
- constexpr uint32_t seq = 15;
+ const uint32_t seq = mSeq++;
int32_t eventId = InputEvent::nextId();
constexpr int32_t deviceId = 1;
constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
@@ -132,20 +163,43 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
- status_t status;
+void InputPublisherAndConsumerTest::publishAndConsumeMotionStream() {
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
- constexpr uint32_t seq = 15;
- int32_t eventId = InputEvent::nextId();
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30}});
+
+ publishAndConsumeMotionEvent(POINTER_1_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300}});
+
+ publishAndConsumeMotionEvent(POINTER_2_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 300, .y = 400}});
+
+ // Provide a consistent input stream - cancel the gesture that was started above
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_CANCEL, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 300, .y = 400}});
+}
+
+void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent(
+ int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) {
constexpr int32_t deviceId = 1;
constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
constexpr std::array<uint8_t, 32> hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
- constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
constexpr int32_t actionButton = 0;
- constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ const size_t pointerCount = pointers.size();
constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
@@ -162,20 +216,21 @@
constexpr float yPrecision = 0.5;
constexpr float xCursorPosition = 1.3;
constexpr float yCursorPosition = 50.6;
- constexpr nsecs_t downTime = 3;
- constexpr size_t pointerCount = 3;
- constexpr nsecs_t eventTime = 4;
- const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
+
+ const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties.push_back({});
pointerProperties[i].clear();
- pointerProperties[i].id = (i + 2) % pointerCount;
+ pointerProperties[i].id = pointers[i].id;
pointerProperties[i].toolType = ToolType::FINGER;
+ pointerCoords.push_back({});
pointerCoords[i].clear();
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i);
+ pointerCoords[i].isResampled = pointers[i].isResampled;
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y);
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i);
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i);
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i);
@@ -185,18 +240,40 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
}
+ publishAndConsumeMotionEvent(deviceId, source, displayId, hmac, action, actionButton, flags,
+ edgeFlags, metaState, buttonState, classification, xScale, yScale,
+ xOffset, yOffset, xPrecision, yPrecision, xCursorPosition,
+ yCursorPosition, rawXScale, rawYScale, rawXOffset, rawYOffset,
+ downTime, eventTime, pointerProperties, pointerCoords);
+}
+
+void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent(
+ int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, float xScale, float yScale,
+ float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
+ float yCursorPosition, float rawXScale, float rawYScale, float rawXOffset, float rawYOffset,
+ nsecs_t downTime, nsecs_t eventTime,
+ const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerCoords>& pointerCoords) {
+ const uint32_t seq = mSeq++;
+ const int32_t eventId = InputEvent::nextId();
ui::Transform transform;
transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
ui::Transform rawTransform;
rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1});
+
+ status_t status;
+ ASSERT_EQ(pointerProperties.size(), pointerCoords.size());
+ const size_t pointerCount = pointerProperties.size();
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
classification, transform, xPrecision, yPrecision,
xCursorPosition, yCursorPosition, rawTransform,
- downTime, eventTime, pointerCount, pointerProperties,
- pointerCoords);
- ASSERT_EQ(OK, status)
- << "publisher publishMotionEvent should return OK";
+ downTime, eventTime, pointerCount,
+ pointerProperties.data(), pointerCoords.data());
+ ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK";
uint32_t consumeSeq;
InputEvent* event;
@@ -280,7 +357,7 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeFocusEvent() {
status_t status;
constexpr uint32_t seq = 15;
@@ -321,7 +398,7 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeCaptureEvent() {
status_t status;
constexpr uint32_t seq = 42;
@@ -361,7 +438,7 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeDragEvent() {
status_t status;
constexpr uint32_t seq = 15;
@@ -405,7 +482,7 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeTouchModeEvent() {
status_t status;
constexpr uint32_t seq = 15;
@@ -462,27 +539,27 @@
}
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionStream());
}
TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishCaptureEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeCaptureEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeDragEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
@@ -546,17 +623,29 @@
}
TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ publishAndConsumeMotionEvent(POINTER_1_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent());
+ publishAndConsumeMotionEvent(POINTER_2_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeCaptureEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeDragEvent());
+ // Provide a consistent input stream - cancel the gesture that was started above
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_CANCEL, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
}
} // namespace android
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 655de80..1cb7f7b 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -27,10 +27,13 @@
namespace android {
+namespace {
+
struct Pointer {
int32_t id;
float x;
float y;
+ ToolType toolType = ToolType::FINGER;
bool isResampled = false;
};
@@ -40,6 +43,8 @@
int32_t action;
};
+} // namespace
+
class TouchResamplingTest : public testing::Test {
protected:
std::unique_ptr<InputPublisher> mPublisher;
@@ -99,7 +104,7 @@
properties.push_back({});
properties.back().clear();
properties.back().id = pointer.id;
- properties.back().toolType = ToolType::FINGER;
+ properties.back().toolType = pointer.toolType;
coords.push_back({});
coords.back().clear();
@@ -292,6 +297,48 @@
}
/**
+ * Stylus pointer coordinates are not resampled, but an event is still generated for the batch with
+ * a resampled timestamp and should be marked as such.
+ */
+TEST_F(TouchResamplingTest, StylusCoordinatesNotResampledFor) {
+ std::chrono::nanoseconds frameTime;
+ std::vector<InputEventEntry> entries, expectedEntries;
+
+ // Initial ACTION_DOWN should be separate, because the first consume event will only return
+ // InputEvent with a single action.
+ entries = {
+ // id x y
+ {0ms, {{0, 10, 20, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_DOWN},
+ };
+ publishInputEventEntries(entries);
+ frameTime = 5ms;
+ expectedEntries = {
+ // id x y
+ {0ms, {{0, 10, 20, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_DOWN},
+ };
+ consumeInputEventEntries(expectedEntries, frameTime);
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ entries = {
+ // id x y
+ {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+ {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+ };
+ publishInputEventEntries(entries);
+ frameTime = 35ms;
+ expectedEntries = {
+ // id x y
+ {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+ {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+ // A resampled event is generated, but the stylus coordinates are not resampled.
+ {25ms,
+ {{0, 30, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE},
+ };
+ consumeInputEventEntries(expectedEntries, frameTime);
+}
+
+/**
* Event should not be resampled when sample time is equal to event time.
*/
TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) {
@@ -544,13 +591,13 @@
// First pointer id=0 leaves the screen
entries = {
// id x y
- {80ms, {{1, 600, 600}}, actionPointer0Up},
+ {80ms, {{0, 120, 120}, {1, 600, 600}}, actionPointer0Up},
};
publishInputEventEntries(entries);
frameTime = 90ms;
expectedEntries = {
// id x y
- {80ms, {{1, 600, 600}}, actionPointer0Up},
+ {80ms, {{0, 120, 120}, {1, 600, 600}}, actionPointer0Up},
// no resampled event for ACTION_POINTER_UP
};
consumeInputEventEntries(expectedEntries, frameTime);
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 8d8a2bc..342f5de 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -73,6 +73,8 @@
"libGLESv2",
],
+ static_libs: ["libguiflags"],
+
export_header_lib_headers: ["jni_headers"],
header_libs: [
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 0f119f3..32fb350 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -19,6 +19,7 @@
#include <android/hardware_buffer.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
+#include <gui/Flags.h>
#include <gui/IGraphicBufferProducer.h>
#include <sys/cdefs.h>
#include <system/graphics.h>
@@ -290,6 +291,20 @@
*/
void releaseConsumerOwnership();
+ /**
+ * Interface for SurfaceTexture callback(s).
+ */
+ struct SurfaceTextureListener : public RefBase {
+ virtual void onFrameAvailable(const BufferItem& item) = 0;
+ virtual void onSetFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) = 0;
+ };
+
+ /**
+ * setSurfaceTextureListener registers a SurfaceTextureListener.
+ */
+ void setSurfaceTextureListener(const sp<SurfaceTextureListener>&);
+
protected:
/**
* abandonLocked overrides the ConsumerBase method to clear
@@ -335,6 +350,14 @@
void computeCurrentTransformMatrixLocked();
/**
+ * onSetFrameRate Notifies the consumer of a setFrameRate call from the producer side.
+ */
+#if FLAG_BQ_SET_FRAME_RATE
+ void onSetFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) override;
+#endif
+
+ /**
* The default consumer usage flags that SurfaceTexture always sets on its
* BufferQueue instance; these will be OR:d with any additional flags passed
* from the SurfaceTexture user. In particular, SurfaceTexture will always
@@ -465,8 +488,30 @@
*/
ImageConsumer mImageConsumer;
+ /**
+ * mSurfaceTextureListener holds the registered SurfaceTextureListener.
+ * Note that SurfaceTexture holds the lister with an sp<>, which means that the listener
+ * must only hold a wp<> to SurfaceTexture and not an sp<>.
+ */
+ sp<SurfaceTextureListener> mSurfaceTextureListener;
+
friend class ImageConsumer;
friend class EGLConsumer;
+
+private:
+ // Proxy listener to avoid having SurfaceTexture directly implement FrameAvailableListener as it
+ // is extending ConsumerBase which also implements FrameAvailableListener.
+ class FrameAvailableListenerProxy : public ConsumerBase::FrameAvailableListener {
+ public:
+ FrameAvailableListenerProxy(const wp<SurfaceTextureListener>& listener)
+ : mSurfaceTextureListener(listener) {}
+
+ private:
+ void onFrameAvailable(const BufferItem& item) override;
+
+ const wp<SurfaceTextureListener> mSurfaceTextureListener;
+ };
+ sp<FrameAvailableListenerProxy> mFrameAvailableListenerProxy;
};
// ----------------------------------------------------------------------------
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 9f610e1..c2535e0 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -23,6 +23,8 @@
#include <system/window.h>
#include <utils/Trace.h>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
// Macros for including the SurfaceTexture name in log messages
@@ -491,4 +493,42 @@
return buffer;
}
+void SurfaceTexture::setSurfaceTextureListener(
+ const sp<android::SurfaceTexture::SurfaceTextureListener>& listener) {
+ SFT_LOGV("setSurfaceTextureListener");
+
+ Mutex::Autolock _l(mMutex);
+ mSurfaceTextureListener = listener;
+ if (mSurfaceTextureListener != nullptr) {
+ mFrameAvailableListenerProxy =
+ sp<FrameAvailableListenerProxy>::make(mSurfaceTextureListener);
+ setFrameAvailableListener(mFrameAvailableListenerProxy);
+ } else {
+ mFrameAvailableListenerProxy.clear();
+ }
+}
+
+void SurfaceTexture::FrameAvailableListenerProxy::onFrameAvailable(const BufferItem& item) {
+ const auto listener = mSurfaceTextureListener.promote();
+ if (listener) {
+ listener->onFrameAvailable(item);
+ }
+}
+
+#if FLAG_BQ_SET_FRAME_RATE
+void SurfaceTexture::onSetFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
+ SFT_LOGV("onSetFrameRate: %.2f", frameRate);
+
+ auto listener = [&] {
+ Mutex::Autolock _l(mMutex);
+ return mSurfaceTextureListener;
+ }();
+
+ if (listener) {
+ listener->onSetFrameRate(frameRate, compatibility, changeFrameRateStrategy);
+ }
+}
+#endif
+
} // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index cc1d12b..2053c6a 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -27,6 +27,7 @@
#include <GrTypes.h>
#include <android-base/stringprintf.h>
#include <gl/GrGLInterface.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <gui/TraceUtils.h>
#include <sync/sync.h>
#include <ui/DebugUtils.h>
@@ -299,10 +300,10 @@
LOG_ALWAYS_FATAL_IF(!glInterface.get(), "GrGLMakeNativeInterface() failed");
SkiaRenderEngine::Contexts contexts;
- contexts.first = GrDirectContext::MakeGL(glInterface, options);
+ contexts.first = GrDirectContexts::MakeGL(glInterface, options);
if (supportsProtectedContentImpl()) {
useProtectedContextImpl(GrProtected::kYes);
- contexts.second = GrDirectContext::MakeGL(glInterface, options);
+ contexts.second = GrDirectContexts::MakeGL(glInterface, options);
useProtectedContextImpl(GrProtected::kNo);
}
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index 7ffdac7..ed3c616 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -48,9 +48,8 @@
void freeHotCacheEntry(android::MultifileHotCache& entry) {
if (entry.entryFd != -1) {
// If we have an fd, then this entry was added to hot cache via INIT or GET
- // We need to unmap and close the entry
+ // We need to unmap the entry
munmap(entry.entryBuffer, entry.entrySize);
- close(entry.entryFd);
} else {
// Otherwise, this was added to hot cache during SET, so it was never mapped
// and fd was only on the deferred thread.
@@ -143,6 +142,7 @@
if (result != sizeof(MultifileHeader)) {
ALOGE("Error reading MultifileHeader from cache entry (%s): %s",
fullPath.c_str(), std::strerror(errno));
+ close(fd);
return;
}
@@ -152,6 +152,7 @@
if (remove(fullPath.c_str()) != 0) {
ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
}
+ close(fd);
continue;
}
@@ -161,6 +162,10 @@
// Memory map the file
uint8_t* mappedEntry = reinterpret_cast<uint8_t*>(
mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0));
+
+ // We can close the file now and the mmap will remain
+ close(fd);
+
if (mappedEntry == MAP_FAILED) {
ALOGE("Failed to mmap cacheEntry, error: %s", std::strerror(errno));
return;
@@ -206,13 +211,11 @@
if (!addToHotCache(entryHash, fd, mappedEntry, fileSize)) {
ALOGE("INIT Failed to add %u to hot cache", entryHash);
munmap(mappedEntry, fileSize);
- close(fd);
return;
}
} else {
// If we're not keeping it in hot cache, unmap it now
munmap(mappedEntry, fileSize);
- close(fd);
}
}
closedir(dir);
@@ -401,9 +404,12 @@
// Memory map the file
cacheEntry =
reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0));
+
+ // We can close the file now and the mmap will remain
+ close(fd);
+
if (cacheEntry == MAP_FAILED) {
ALOGE("Failed to mmap cacheEntry, error: %s", std::strerror(errno));
- close(fd);
return 0;
}
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index dbee13b..1639be6 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -42,6 +42,8 @@
virtual void TearDown() { mMBC.reset(); }
+ int getFileDescriptorCount();
+
std::unique_ptr<TemporaryFile> mTempFile;
std::unique_ptr<MultifileBlobCache> mMBC;
};
@@ -216,4 +218,56 @@
ASSERT_EQ('y', buf[0]);
}
+int MultifileBlobCacheTest::getFileDescriptorCount() {
+ DIR* directory = opendir("/proc/self/fd");
+
+ int fileCount = 0;
+ struct dirent* entry;
+ while ((entry = readdir(directory)) != NULL) {
+ fileCount++;
+ // printf("File: %s\n", entry->d_name);
+ }
+
+ closedir(directory);
+ return fileCount;
+}
+
+TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) {
+ // Populate the cache with a bunch of entries
+ size_t kLargeNumberOfEntries = 1024;
+ for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ // printf("Caching: %i", i);
+
+ // Use the index as the key and value
+ mMBC->set(&i, sizeof(i), &i, sizeof(i));
+
+ int result = 0;
+ ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
+ ASSERT_EQ(i, result);
+ }
+
+ // Ensure we don't have a bunch of open fds
+ ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Now open it again and ensure we still don't have a bunch of open fds
+ mMBC.reset(
+ new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0]));
+
+ // Check after initialization
+ ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+
+ for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ int result = 0;
+ ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
+ ASSERT_EQ(i, result);
+ }
+
+ // And again after we've actually used it
+ ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+}
+
} // namespace android
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index dd3cc3b..141fe02 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -77,7 +77,7 @@
mInitialized.store(true);
}
-void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+void GpuMem::setGpuMemTotalMap(bpf::BpfMapRO<uint64_t, uint64_t>& map) {
mGpuMemTotalMap = std::move(map);
}
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
index 7588b54..9aa74d6 100644
--- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -44,12 +44,12 @@
friend class TestableGpuMem;
// set gpu memory total map
- void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map);
+ void setGpuMemTotalMap(bpf::BpfMapRO<uint64_t, uint64_t>& map);
// indicate whether ebpf has been initialized
std::atomic<bool> mInitialized = false;
// bpf map for GPU memory total data
- android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap;
+ android::bpf::BpfMapRO<uint64_t, uint64_t> mGpuMemTotalMap;
// gpu memory tracepoint event category
static constexpr char kGpuMemTraceGroup[] = "gpu_mem";
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index 8dabe4f..1f5b288 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -66,9 +66,7 @@
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
mTestableGpuMem.setInitialized();
errno = 0;
- mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH,
- TEST_MAP_SIZE,
- BPF_F_NO_PREALLOC));
+ mTestMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
EXPECT_EQ(0, errno);
EXPECT_TRUE(mTestMap.isValid());
diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
index 5c04210..6550df9 100644
--- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
@@ -65,9 +65,7 @@
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
errno = 0;
- mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH,
- TEST_MAP_SIZE,
- BPF_F_NO_PREALLOC));
+ mTestMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
EXPECT_EQ(0, errno);
EXPECT_TRUE(mTestMap.isValid());
diff --git a/services/gpuservice/tests/unittests/TestableGpuMem.h b/services/gpuservice/tests/unittests/TestableGpuMem.h
index 6c8becb..f21843f 100644
--- a/services/gpuservice/tests/unittests/TestableGpuMem.h
+++ b/services/gpuservice/tests/unittests/TestableGpuMem.h
@@ -28,7 +28,7 @@
void setInitialized() { mGpuMem->mInitialized.store(true); }
- void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ void setGpuMemTotalMap(bpf::BpfMapRO<uint64_t, uint64_t>& map) {
mGpuMem->setGpuMemTotalMap(map);
}
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index 5693848..f848c82 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -17,6 +17,7 @@
#pragma once
#include <condition_variable>
+#include <functional>
#include <list>
#include <mutex>
#include <optional>
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 56a3fb4..8e04150 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -64,14 +64,13 @@
class : public InputDeviceMetricsLogger {
nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
- void logInputDeviceUsageReported(const InputDeviceInfo& info,
+ void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
const DeviceUsageReport& report) override {
const int32_t durationMillis =
std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
const static std::vector<int32_t> empty;
- const auto& identifier = info.getIdentifier();
- ALOGD_IF(DEBUG, "Usage session reported for device: %s", identifier.name.c_str());
+ ALOGD_IF(DEBUG, "Usage session reported for device id: %d", info.deviceId);
ALOGD_IF(DEBUG, " Total duration: %dms", durationMillis);
ALOGD_IF(DEBUG, " Source breakdown:");
@@ -96,11 +95,9 @@
ALOGD_IF(DEBUG, " - uid: %s\t duration: %dms", uid.toString().c_str(),
durMillis);
}
- util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
- identifier.version,
- linuxBusToInputDeviceBusEnum(identifier.bus,
- info.getUsiVersion().has_value()),
- durationMillis, sources, durationsPerSource, uids, durationsPerUid);
+ util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, info.vendor, info.product, info.version,
+ linuxBusToInputDeviceBusEnum(info.bus, info.isUsiStylus), durationMillis,
+ sources, durationsPerSource, uids, durationsPerUid);
}
} sStatsdLogger;
@@ -116,7 +113,7 @@
} // namespace
-InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo& info,
+InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType,
const NotifyKeyArgs& keyArgs) {
if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
return InputDeviceUsageSource::UNKNOWN;
@@ -132,7 +129,7 @@
return InputDeviceUsageSource::GAMEPAD;
}
- if (info.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+ if (keyboardType == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
return InputDeviceUsageSource::KEYBOARD;
}
@@ -232,8 +229,8 @@
void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
reportCompletedSessions();
- const SourceProvider getSources = [&args](const InputDeviceInfo& info) {
- return std::set{getUsageSourceForKeyArgs(info, args)};
+ const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
+ return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
};
onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
@@ -291,13 +288,23 @@
}
void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
- std::map<DeviceId, InputDeviceInfo> newDeviceInfos;
+ std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
for (const InputDeviceInfo& info : infos) {
if (isIgnoredInputDeviceId(info.getId())) {
continue;
}
- newDeviceInfos.emplace(info.getId(), info);
+ const auto& i = info.getIdentifier();
+ newDeviceInfos.emplace(info.getId(),
+ MetricsDeviceInfo{
+ .deviceId = info.getId(),
+ .vendor = i.vendor,
+ .product = i.product,
+ .version = i.version,
+ .bus = i.bus,
+ .isUsiStylus = info.getUsiVersion().has_value(),
+ .keyboardType = info.getKeyboardType(),
+ });
}
for (auto [deviceId, info] : mLoggedDeviceInfos) {
@@ -311,7 +318,7 @@
}
void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
- const InputDeviceInfo& info) {
+ const MetricsDeviceInfo& info) {
auto it = mActiveUsageSessions.find(deviceId);
if (it == mActiveUsageSessions.end()) {
return;
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 1f7c5d9..7775087 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -79,7 +79,7 @@
};
/** Returns the InputDeviceUsageSource that corresponds to the key event. */
-InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo&, const NotifyKeyArgs&);
+InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType, const NotifyKeyArgs&);
/** Returns the InputDeviceUsageSources that correspond to the motion event. */
std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
@@ -110,7 +110,19 @@
UidUsageBreakdown uidBreakdown;
};
- virtual void logInputDeviceUsageReported(const InputDeviceInfo&, const DeviceUsageReport&) = 0;
+ // A subset of information from the InputDeviceInfo class that is used for metrics collection,
+ // used to avoid copying and storing all of the fields and strings in InputDeviceInfo.
+ struct MetricsDeviceInfo {
+ int32_t deviceId;
+ int32_t vendor;
+ int32_t product;
+ int32_t version;
+ int32_t bus;
+ bool isUsiStylus;
+ int32_t keyboardType;
+ };
+ virtual void logInputDeviceUsageReported(const MetricsDeviceInfo&,
+ const DeviceUsageReport&) = 0;
virtual ~InputDeviceMetricsLogger() = default;
};
@@ -153,8 +165,9 @@
}
using Uid = gui::Uid;
+ using MetricsDeviceInfo = InputDeviceMetricsLogger::MetricsDeviceInfo;
- std::map<DeviceId, InputDeviceInfo> mLoggedDeviceInfos;
+ std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos;
using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>;
SyncQueue<Interaction> mInteractionsQueue;
@@ -188,8 +201,9 @@
std::map<DeviceId, ActiveSession> mActiveUsageSessions;
void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos);
- void onInputDeviceRemoved(DeviceId deviceId, const InputDeviceInfo& info);
- using SourceProvider = std::function<std::set<InputDeviceUsageSource>(const InputDeviceInfo&)>;
+ void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info);
+ using SourceProvider =
+ std::function<std::set<InputDeviceUsageSource>(const MetricsDeviceInfo&)>;
void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
const SourceProvider& getSources);
void onInputDeviceInteraction(const Interaction&);
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index f889de5..0f62324 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -18,6 +18,7 @@
#include "UnwantedInteractionBlocker.h"
#include <android-base/stringprintf.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
#include <inttypes.h>
@@ -28,6 +29,8 @@
#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h"
+namespace input_flags = com::android::input::flags;
+
using android::base::StringPrintf;
/**
@@ -344,10 +347,14 @@
ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
{ // acquire lock
std::scoped_lock lock(mLock);
- const std::vector<NotifyMotionArgs> processedArgs =
- mPreferStylusOverTouchBlocker.processMotion(args);
- for (const NotifyMotionArgs& loopArgs : processedArgs) {
- notifyMotionLocked(loopArgs);
+ if (input_flags::enable_multi_device_input()) {
+ notifyMotionLocked(args);
+ } else {
+ const std::vector<NotifyMotionArgs> processedArgs =
+ mPreferStylusOverTouchBlocker.processMotion(args);
+ for (const NotifyMotionArgs& loopArgs : processedArgs) {
+ notifyMotionLocked(loopArgs);
+ }
}
} // release lock
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f037b87..4251682 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1294,7 +1294,7 @@
}
std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked(
- int32_t displayId, const sp<WindowInfoHandle>& touchedWindow) const {
+ int32_t displayId, const sp<WindowInfoHandle>& touchedWindow, int32_t pointerId) const {
if (touchedWindow == nullptr) {
return {};
}
@@ -1310,9 +1310,10 @@
const WindowInfo& info = *windowHandle->getInfo();
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
- addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
- /*pointerIds=*/{}, /*firstDownTimeInTarget=*/std::nullopt,
- outsideTargets);
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ pointerIds.set(pointerId);
+ addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds,
+ /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
}
}
return outsideTargets;
@@ -2334,6 +2335,7 @@
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
const auto [x, y] = resolveTouchedPosition(entry);
const int32_t pointerIndex = MotionEvent::getActionIndex(action);
+ const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
// Outside targets should be added upon first dispatched DOWN event. That means, this should
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2341,7 +2343,7 @@
findTouchedWindowAtLocked(displayId, x, y, isStylus);
if (isDown) {
- targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle);
+ targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointerId);
}
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
@@ -2399,7 +2401,6 @@
if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- const int32_t pointerId = entry.pointerProperties[0].id;
// The "windowHandle" is the target of this hovering pointer.
tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
}
@@ -2424,7 +2425,7 @@
// Update the temporary touch state.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
if (!isHoverAction) {
- pointerIds.set(entry.pointerProperties[pointerIndex].id);
+ pointerIds.set(pointerId);
}
const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
@@ -2468,7 +2469,6 @@
// If a window is already pilfering some pointers, give it this new pointer as well and
// make it pilfering. This will prevent other non-spy windows from getting this pointer,
// which is a specific behaviour that we want.
- const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) &&
touchedWindow.hasPilferingPointers(entry.deviceId)) {
@@ -4321,9 +4321,9 @@
mVerifiersByDisplay.try_emplace(args.displayId,
StringPrintf("display %" PRId32, args.displayId));
Result<void> result =
- it->second.processMovement(args.deviceId, args.action, args.getPointerCount(),
- args.pointerProperties.data(), args.pointerCoords.data(),
- args.flags);
+ it->second.processMovement(args.deviceId, args.source, args.action,
+ args.getPointerCount(), args.pointerProperties.data(),
+ args.pointerCoords.data(), args.flags);
if (!result.ok()) {
LOG(FATAL) << "Bad stream: " << result.error() << " caused by " << args.dump();
}
@@ -4528,7 +4528,7 @@
// the injected event, it is responsible for setting POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY.
// For those events, we will set FLAG_IS_ACCESSIBILITY_EVENT to allow apps to distinguish them
// from events that originate from actual hardware.
- int32_t resolvedDeviceId = VIRTUAL_KEYBOARD_ID;
+ DeviceId resolvedDeviceId = VIRTUAL_KEYBOARD_ID;
if (policyFlags & POLICY_FLAG_FILTERED) {
resolvedDeviceId = event->getDeviceId();
}
@@ -5915,14 +5915,16 @@
status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) {
const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token);
if (!requestingChannel) {
- ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token");
+ LOG(WARNING)
+ << "Attempted to pilfer pointers from an un-registered channel or invalid token";
return BAD_VALUE;
}
auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
if (statePtr == nullptr || windowPtr == nullptr) {
- ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
- " Ignoring.");
+ LOG(WARNING)
+ << "Attempted to pilfer points from a channel without any on-going pointer streams."
+ " Ignoring.";
return BAD_VALUE;
}
std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds();
@@ -5931,36 +5933,38 @@
<< " in window: " << windowPtr->dump();
return BAD_VALUE;
}
- const int32_t deviceId = *deviceIds.begin();
- TouchState& state = *statePtr;
- TouchedWindow& window = *windowPtr;
- // Send cancel events to all the input channels we're stealing from.
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "input channel stole pointer stream");
- options.deviceId = deviceId;
- options.displayId = displayId;
- std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId);
- options.pointerIds = pointerIds;
- std::string canceledWindows;
- for (const TouchedWindow& w : state.windows) {
- const std::shared_ptr<InputChannel> channel =
- getInputChannelLocked(w.windowHandle->getToken());
- if (channel != nullptr && channel->getConnectionToken() != token) {
- synthesizeCancelationEventsForInputChannelLocked(channel, options);
- canceledWindows += canceledWindows.empty() ? "[" : ", ";
- canceledWindows += channel->getName();
+ for (const DeviceId deviceId : deviceIds) {
+ TouchState& state = *statePtr;
+ TouchedWindow& window = *windowPtr;
+ // Send cancel events to all the input channels we're stealing from.
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "input channel stole pointer stream");
+ options.deviceId = deviceId;
+ options.displayId = displayId;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId);
+ options.pointerIds = pointerIds;
+ std::string canceledWindows;
+ for (const TouchedWindow& w : state.windows) {
+ const std::shared_ptr<InputChannel> channel =
+ getInputChannelLocked(w.windowHandle->getToken());
+ if (channel != nullptr && channel->getConnectionToken() != token) {
+ synthesizeCancelationEventsForInputChannelLocked(channel, options);
+ canceledWindows += canceledWindows.empty() ? "[" : ", ";
+ canceledWindows += channel->getName();
+ }
}
+ canceledWindows += canceledWindows.empty() ? "[]" : "]";
+ LOG(INFO) << "Channel " << requestingChannel->getName()
+ << " is stealing input gesture for device " << deviceId << " from "
+ << canceledWindows;
+
+ // Prevent the gesture from being sent to any other windows.
+ // This only blocks relevant pointers to be sent to other windows
+ window.addPilferingPointers(deviceId, pointerIds);
+
+ state.cancelPointersForWindowsExcept(deviceId, pointerIds, token);
}
- canceledWindows += canceledWindows.empty() ? "[]" : "]";
- ALOGI("Channel %s is stealing touch from %s", requestingChannel->getName().c_str(),
- canceledWindows.c_str());
-
- // Prevent the gesture from being sent to any other windows.
- // This only blocks relevant pointers to be sent to other windows
- window.addPilferingPointers(deviceId, pointerIds);
-
- state.cancelPointersForWindowsExcept(deviceId, pointerIds, token);
return OK;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 00c2acb..62e2d58 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -242,8 +242,8 @@
int32_t displayId, float x, float y, bool isStylus = false,
bool ignoreDragWindow = false) const REQUIRES(mLock);
std::vector<InputTarget> findOutsideTargetsLocked(
- int32_t displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow) const
- REQUIRES(mLock);
+ int32_t displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow,
+ int32_t pointerId) const REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index ccffe26..b21427d 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -83,6 +83,11 @@
}
}
+/**
+ * Return:
+ * true if the incoming event was correctly tracked,
+ * false if the incoming event should be dropped.
+ */
bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
switch (actionMasked) {
@@ -310,7 +315,7 @@
nsecs_t currentTime) {
std::vector<std::unique_ptr<EventEntry>> events;
for (MotionMemento& memento : mMotionMementos) {
- if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
+ if (!isFromSource(memento.source, AINPUT_SOURCE_CLASS_POINTER)) {
continue;
}
@@ -443,7 +448,7 @@
MotionMemento& memento = mMotionMementos[i];
// Since we support split pointers we need to merge touch events
// from the same source + device + screen.
- if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
+ if (isFromSource(memento.source, AINPUT_SOURCE_CLASS_POINTER)) {
bool merged = false;
for (size_t j = 0; j < other.mMotionMementos.size(); j++) {
MotionMemento& otherMemento = other.mMotionMementos[j];
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 11f3413..343630c 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -16,7 +16,9 @@
#include "InputTarget.h"
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <input/PrintTools.h>
#include <inttypes.h>
#include <string>
@@ -34,7 +36,10 @@
}
// Ensure that the new set of pointers doesn't overlap with the current set of pointers.
- LOG_ALWAYS_FATAL_IF((pointerIds & newPointerIds).any());
+ if ((pointerIds & newPointerIds).any()) {
+ LOG(FATAL) << __func__ << " - overlap with incoming pointers "
+ << bitsetToString(newPointerIds) << " in " << *this;
+ }
pointerIds |= newPointerIds;
for (size_t i = 0; i < newPointerIds.size(); i++) {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 58b29b8..531fc67 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -451,7 +451,8 @@
void KeyboardInputMapper::onKeyDownProcessed() {
InputReaderContext& context = *getContext();
if (context.isPreventingTouchpadTaps()) {
- // avoid pinging java service unnecessarily
+ // avoid pinging java service unnecessarily, just fade pointer again if it became visible
+ context.fadePointer();
return;
}
// Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 6774b17..e630915 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -28,6 +28,7 @@
namespace android {
+using testing::AllOf;
using testing::Return;
using testing::VariantWith;
constexpr auto ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
@@ -36,6 +37,7 @@
constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS;
constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
+constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION;
/**
* Unit tests for CursorInputMapper.
@@ -58,10 +60,23 @@
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL))
.WillRepeatedly(Return(false));
- EXPECT_CALL(mMockInputReaderContext, bumpGeneration()).WillRepeatedly(Return(1));
-
mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
}
+
+ void setPointerCapture(bool enabled) {
+ mReaderConfiguration.pointerCaptureRequest.enable = enabled;
+ mReaderConfiguration.pointerCaptureRequest.seq = 1;
+ int32_t generation = mDevice->getGeneration();
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::POINTER_CAPTURE);
+ ASSERT_THAT(args,
+ ElementsAre(
+ VariantWith<NotifyDeviceResetArgs>(AllOf(WithDeviceId(DEVICE_ID)))));
+
+ // Check that generation also got bumped
+ ASSERT_GT(mDevice->getGeneration(), generation);
+ }
};
/**
@@ -102,4 +117,83 @@
VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
}
+/**
+ * Set pointer capture and check that ACTION_MOVE events are emitted from CursorInputMapper.
+ * During pointer capture, source should be set to MOUSE_RELATIVE. When the capture is disabled,
+ * the events should be generated normally:
+ * 1) The source should return to SOURCE_MOUSE
+ * 2) Cursor position should be incremented by the relative device movements
+ * 3) Cursor position of NotifyMotionArgs should now be getting populated.
+ * When it's not SOURCE_MOUSE, CursorInputMapper doesn't populate cursor position values.
+ */
+TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) {
+ setPointerCapture(true);
+ std::list<NotifyArgs> args;
+
+ // Move.
+ args += process(EV_REL, REL_X, 10);
+ args += process(EV_REL, REL_Y, 20);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), WithCoords(10.0f, 20.0f),
+ WithCursorPosition(INVALID_CURSOR_POSITION,
+ INVALID_CURSOR_POSITION)))));
+
+ // Button press.
+ args.clear();
+ args += process(EV_KEY, BTN_MOUSE, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_PRESS),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+
+ // Button release.
+ args.clear();
+ args += process(EV_KEY, BTN_MOUSE, 0);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_RELEASE),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_UP),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+
+ // Another move.
+ args.clear();
+ args += process(EV_REL, REL_X, 30);
+ args += process(EV_REL, REL_Y, 40);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithCoords(30.0f, 40.0f)))));
+
+ // Disable pointer capture. Afterwards, events should be generated the usual way.
+ setPointerCapture(false);
+
+ args.clear();
+ args += process(EV_REL, REL_X, 10);
+ args += process(EV_REL, REL_Y, 20);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithCoords(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f),
+ WithCursorPosition(INITIAL_CURSOR_X + 10.0f,
+ INITIAL_CURSOR_Y + 20.0f)))));
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index 7ccfaca..fdf9ed1 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -66,21 +66,14 @@
}
InputDeviceInfo generateTestDeviceInfo(int32_t id = DEVICE_ID,
- uint32_t sources = TOUCHSCREEN | STYLUS,
- bool isAlphabetic = false) {
+ uint32_t sources = TOUCHSCREEN | STYLUS) {
auto info = InputDeviceInfo();
info.initialize(id, /*generation=*/1, /*controllerNumber=*/1, generateTestIdentifier(id),
"alias", /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
info.addSource(sources);
- info.setKeyboardType(isAlphabetic ? AINPUT_KEYBOARD_TYPE_ALPHABETIC
- : AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
return info;
}
-const InputDeviceInfo ALPHABETIC_KEYBOARD_INFO =
- generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/true);
-const InputDeviceInfo NON_ALPHABETIC_KEYBOARD_INFO =
- generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/false);
const InputDeviceInfo TOUCHSCREEN_STYLUS_INFO = generateTestDeviceInfo(DEVICE_ID);
const InputDeviceInfo SECOND_TOUCHSCREEN_STYLUS_INFO = generateTestDeviceInfo(DEVICE_ID_2);
@@ -106,7 +99,7 @@
switch (usageSource) {
case InputDeviceUsageSource::UNKNOWN: {
ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
- getUsageSourceForKeyArgs(generateTestDeviceInfo(),
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NONE,
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
.build()));
@@ -123,7 +116,7 @@
case InputDeviceUsageSource::BUTTONS: {
ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
- getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
.keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
.build()));
@@ -132,7 +125,7 @@
case InputDeviceUsageSource::KEYBOARD: {
ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
- getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
.build()));
break;
@@ -140,13 +133,13 @@
case InputDeviceUsageSource::DPAD: {
ASSERT_EQ(InputDeviceUsageSource::DPAD,
- getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
.keyCode(AKEYCODE_DPAD_CENTER)
.build()));
ASSERT_EQ(InputDeviceUsageSource::DPAD,
- getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
.keyCode(AKEYCODE_DPAD_CENTER)
.build()));
@@ -155,13 +148,13 @@
case InputDeviceUsageSource::GAMEPAD: {
ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
- getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
.keyCode(AKEYCODE_BUTTON_A)
.build()));
ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
- getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
.keyCode(AKEYCODE_BUTTON_A)
.build()));
@@ -358,7 +351,13 @@
std::optional<UidUsageBreakdown> uidBreakdown = {}) {
ASSERT_GE(mLoggedUsageSessions.size(), 1u);
const auto& [loggedInfo, report] = *mLoggedUsageSessions.begin();
- ASSERT_EQ(info.getIdentifier(), loggedInfo.getIdentifier());
+ const auto& i = info.getIdentifier();
+ ASSERT_EQ(info.getId(), loggedInfo.deviceId);
+ ASSERT_EQ(i.vendor, loggedInfo.vendor);
+ ASSERT_EQ(i.product, loggedInfo.product);
+ ASSERT_EQ(i.version, loggedInfo.version);
+ ASSERT_EQ(i.bus, loggedInfo.bus);
+ ASSERT_EQ(info.getUsiVersion().has_value(), loggedInfo.isUsiStylus);
ASSERT_EQ(duration, report.usageDuration);
if (sourceBreakdown) {
ASSERT_EQ(sourceBreakdown, report.sourceBreakdown);
@@ -389,12 +388,12 @@
}
private:
- std::vector<std::tuple<InputDeviceInfo, DeviceUsageReport>> mLoggedUsageSessions;
+ std::vector<std::tuple<MetricsDeviceInfo, DeviceUsageReport>> mLoggedUsageSessions;
nanoseconds mCurrentTime{TIME};
nanoseconds getCurrentTime() override { return mCurrentTime; }
- void logInputDeviceUsageReported(const InputDeviceInfo& info,
+ void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
const DeviceUsageReport& report) override {
mLoggedUsageSessions.emplace_back(info, report);
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 38970f2..f0602df 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -28,6 +28,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/Input.h>
+#include <input/PrintTools.h>
#include <linux/input.h>
#include <sys/epoll.h>
@@ -50,6 +51,8 @@
using namespace ftl::flag_operators;
using testing::AllOf;
+namespace {
+
// An arbitrary time value.
static constexpr nsecs_t ARBITRARY_TIME = 1234;
@@ -136,6 +139,10 @@
auto operator<=>(const PointF&) const = default;
};
+inline std::string pointFToString(const PointF& p) {
+ return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";
+}
+
/**
* Return a DOWN key event with KEYCODE_A.
*/
@@ -148,13 +155,8 @@
return event;
}
-static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) {
- ASSERT_EQ(expectedAction, receivedAction)
- << "expected " << MotionEvent::actionToString(expectedAction) << ", got "
- << MotionEvent::actionToString(receivedAction);
-}
-
MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
+ *result_listener << "expected downTime " << downTime << ", but got " << arg.getDownTime();
return arg.getDownTime() == downTime;
}
@@ -165,6 +167,7 @@
}
MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
+ *result_listener << "expected flags " << std::hex << flags << ", but got " << arg.getFlags();
return arg.getFlags() == flags;
}
@@ -173,10 +176,16 @@
*result_listener << "Expected 1 pointer, got " << arg.getPointerCount();
return false;
}
- return arg.getX(/*pointerIndex=*/0) == x && arg.getY(/*pointerIndex=*/0) == y;
+ const float receivedX = arg.getX(/*pointerIndex=*/0);
+ const float receivedY = arg.getY(/*pointerIndex=*/0);
+ *result_listener << "expected coords (" << x << ", " << y << "), but got (" << receivedX << ", "
+ << receivedY << ")";
+ return receivedX == x && receivedY == y;
}
MATCHER_P(WithPointerCount, pointerCount, "MotionEvent with specified number of pointers") {
+ *result_listener << "expected pointerCount " << pointerCount << ", but got "
+ << arg.getPointerCount();
return arg.getPointerCount() == pointerCount;
}
@@ -187,6 +196,8 @@
const int32_t pointerId = arg.getPointerId(pointerIndex);
actualPointers[pointerId] = {arg.getX(pointerIndex), arg.getY(pointerIndex)};
}
+ *result_listener << "expected pointers " << dumpMap(pointers, constToString, pointFToString)
+ << ", but got " << dumpMap(actualPointers, constToString, pointFToString);
return pointers == actualPointers;
}
@@ -617,6 +628,7 @@
mFilteredEvent = nullptr;
}
};
+} // namespace
// --- InputDispatcherTest ---
@@ -959,7 +971,7 @@
switch (expectedEventType) {
case InputEventType::KEY: {
const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
- EXPECT_EQ(expectedAction, keyEvent.getAction());
+ ASSERT_THAT(keyEvent, WithKeyAction(expectedAction));
if (expectedFlags.has_value()) {
EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
}
@@ -967,8 +979,7 @@
}
case InputEventType::MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- assertMotionAction(expectedAction, motionEvent.getAction());
-
+ ASSERT_THAT(motionEvent, WithMotionAction(expectedAction));
if (expectedFlags.has_value()) {
EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
}
@@ -6467,7 +6478,7 @@
ASSERT_NE(nullptr, motionEvent)
<< name.c_str() << ": consumer should have returned non-NULL event.";
- assertMotionAction(expectedAction, motionEvent->getAction());
+ ASSERT_THAT(*motionEvent, WithMotionAction(expectedAction));
ASSERT_EQ(points.size(), motionEvent->getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
@@ -6848,6 +6859,10 @@
// We have a focused application, but no focused window
// Make sure that we don't notify policy twice about the same ANR.
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
+ const std::chrono::duration appTimeout = 400ms;
+ mApplication->setDispatchingTimeout(appTimeout);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+
mWindow->setFocusable(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
@@ -6855,13 +6870,18 @@
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
+ const std::chrono::duration eventInjectionTimeout = 100ms;
+ ASSERT_LT(eventInjectionTimeout, appTimeout);
const InputEventInjectionResult result =
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 100ms, /*allowKeyRepeat=*/false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
- const std::chrono::duration appTimeout =
- mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(appTimeout, mApplication);
+ InputEventInjectionSync::WAIT_FOR_RESULT, eventInjectionTimeout,
+ /*allowKeyRepeat=*/false);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result)
+ << "result=" << ftl::enum_string(result);
+ // We already waited for 'eventInjectionTimeout`, because the countdown started when the event
+ // was first injected. So now we have (appTimeout - eventInjectionTimeout) left to wait.
+ std::chrono::duration remainingWaitTime = appTimeout - eventInjectionTimeout;
+ mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(remainingWaitTime, mApplication);
std::this_thread::sleep_for(appTimeout);
// ANR should not be raised again. It is up to policy to do that if it desires.
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 0eee2b9..dac4ea0 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -27,7 +27,7 @@
void InputMapperUnitTest::SetUp() {
mFakePointerController = std::make_shared<FakePointerController>();
mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(400, 240);
+ mFakePointerController->setPosition(INITIAL_CURSOR_X, INITIAL_CURSOR_Y);
EXPECT_CALL(mMockInputReaderContext, getPointerController(DEVICE_ID))
.WillRepeatedly(Return(mFakePointerController));
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index 909bd9c..c2ac258 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -39,6 +39,8 @@
protected:
static constexpr int32_t EVENTHUB_ID = 1;
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr float INITIAL_CURSOR_X = 400;
+ static constexpr float INITIAL_CURSOR_Y = 240;
virtual void SetUp() override;
void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 6539593..6032e30 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -4744,92 +4744,6 @@
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
-TEST_F(CursorInputMapperTest, Process_PointerCapture) {
- addConfigurationProperty("cursor.mode", "pointer");
- mFakePolicy->setPointerCapture(true);
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyDeviceResetArgs resetArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
- NotifyMotionArgs args;
-
- // Move.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 10.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(100.0f, 200.0f));
-
- // Button press.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- // Button release.
- process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-
- // Another move.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 30);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 40);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 30.0f, 40.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(100.0f, 200.0f));
-
- // Disable pointer capture and check that the device generation got bumped
- // and events are generated the usual way.
- const uint32_t generation = mReader->getContext()->getGeneration();
- mFakePolicy->setPointerCapture(false);
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
-}
-
/**
* When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
* pointer acceleration or speed processing should not be applied.
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 5d50b94..05823cd 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -59,7 +59,7 @@
(int32_t deviceId), (override));
MOCK_METHOD(void, requestTimeoutAtTime, (nsecs_t when), (override));
- MOCK_METHOD(int32_t, bumpGeneration, (), (override));
+ int32_t bumpGeneration() override { return ++mGeneration; }
MOCK_METHOD(void, getExternalStylusDevices, (std::vector<InputDeviceInfo> & outDevices),
(override));
@@ -76,6 +76,9 @@
MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override));
MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override));
+
+private:
+ int32_t mGeneration = 0;
};
class MockEventHubInterface : public EventHubInterface {
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 08a5559..48f5673 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -112,6 +112,15 @@
}
/**
+ * Pointer should still hide if touchpad taps are already disabled
+ */
+TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithTouchpadTapDisabledHidePointer) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).WillRepeatedly(Return(true));
+ testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false);
+}
+
+/**
* Pointer visibility should remain unaffected by meta keys even if Input Method Connection is
* active
*/
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 183383f..31ad569 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -185,6 +185,14 @@
return argX == x && argY == y;
}
+MATCHER_P2(WithCursorPosition, x, y, "InputEvent with specified cursor position") {
+ const auto argX = arg.xCursorPosition;
+ const auto argY = arg.yCursorPosition;
+ *result_listener << "expected cursor position (" << x << ", " << y << "), but got (" << argX
+ << ", " << argY << ")";
+ return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY);
+}
+
MATCHER_P3(WithPointerCoords, pointer, x, y, "InputEvent with specified coords for pointer") {
const auto argX = arg.pointerCoords[pointer].getX();
const auto argY = arg.pointerCoords[pointer].getY();
diff --git a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
index e9016bb..219b662 100644
--- a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
@@ -50,8 +50,9 @@
// Pops blocks if it is empty, so only pop up to num elements inserted.
size_t numPops = fdp.ConsumeIntegralInRange<size_t>(0, filled);
for (size_t i = 0; i < numPops; i++) {
- queue.popWithTimeout(
- std::chrono::nanoseconds{fdp.ConsumeIntegral<int64_t>()});
+ // Provide a random timeout up to 1 second
+ queue.popWithTimeout(std::chrono::nanoseconds(
+ fdp.ConsumeIntegralInRange<int64_t>(0, 1E9)));
}
filled > numPops ? filled -= numPops : filled = 0;
},
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
index f8ebc97..3b3ed9b 100644
--- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -73,9 +73,11 @@
},
[&]() -> void {
// SendToNextStage_NotifyKeyArgs
- const nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
- const nsecs_t readTime =
- eventTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8);
+ const nsecs_t eventTime =
+ fdp.ConsumeIntegralInRange<nsecs_t>(0,
+ systemTime(SYSTEM_TIME_MONOTONIC));
+ const nsecs_t readTime = fdp.ConsumeIntegralInRange<
+ nsecs_t>(eventTime, std::numeric_limits<nsecs_t>::max());
mClassifier->notifyKey({/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(),
eventTime, readTime,
/*deviceId=*/fdp.ConsumeIntegral<int32_t>(),
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a73c511..5890050 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3183,8 +3183,7 @@
} else {
// release sideband stream if it exists and a non null buffer is being set
if (mDrawingState.sidebandStream != nullptr) {
- mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
- mDrawingState.sidebandStream = nullptr;
+ setSidebandStream(nullptr, info, postTime);
}
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 21714da..edab7ec 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -171,10 +171,8 @@
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
- ResyncCallback resyncCallback,
EventRegistrationFlags eventRegistration)
- : resyncCallback(std::move(resyncCallback)),
- mOwnerUid(callingUid),
+ : mOwnerUid(callingUid),
mEventRegistration(eventRegistration),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -250,9 +248,7 @@
EventThread::EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule> vsyncSchedule,
android::frametimeline::TokenManager* tokenManager,
- ThrottleVsyncCallback throttleVsyncCallback,
- GetVsyncPeriodFunction getVsyncPeriodFunction,
- std::chrono::nanoseconds workDuration,
+ IEventThreadCallback& callback, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration)
: mThreadName(name),
mVsyncTracer(base::StringPrintf("VSYNC-%s", name), 0),
@@ -261,11 +257,7 @@
mVsyncSchedule(std::move(vsyncSchedule)),
mVsyncRegistration(mVsyncSchedule->getDispatch(), createDispatchCallback(), name),
mTokenManager(tokenManager),
- mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
- mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)) {
- LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
- "getVsyncPeriodFunction must not be null");
-
+ mCallback(callback) {
mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
std::unique_lock<std::mutex> lock(mMutex);
threadMain(lock);
@@ -307,10 +299,10 @@
}
sp<EventThreadConnection> EventThread::createEventConnection(
- ResyncCallback resyncCallback, EventRegistrationFlags eventRegistration) const {
+ EventRegistrationFlags eventRegistration) const {
return sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
IPCThreadState::self()->getCallingUid(),
- std::move(resyncCallback), eventRegistration);
+ eventRegistration);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -353,9 +345,7 @@
}
void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
- if (connection->resyncCallback) {
- connection->resyncCallback();
- }
+ mCallback.resync();
std::lock_guard<std::mutex> lock(mMutex);
@@ -371,20 +361,18 @@
const sp<EventThreadConnection>& connection) const {
// Resync so that the vsync is accurate with hardware. getLatestVsyncEventData is an alternate
// way to get vsync data (instead of posting callbacks to Choreographer).
- if (connection->resyncCallback) {
- connection->resyncCallback();
- }
+ mCallback.resync();
VsyncEventData vsyncEventData;
- nsecs_t frameInterval = mGetVsyncPeriodFunction(connection->mOwnerUid);
- vsyncEventData.frameInterval = frameInterval;
+ const Period frameInterval = mCallback.getVsyncPeriod(connection->mOwnerUid);
+ vsyncEventData.frameInterval = frameInterval.ns();
const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> {
std::lock_guard<std::mutex> lock(mMutex);
const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(
systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
return {vsyncTime, vsyncTime - mReadyDuration.count()};
}();
- generateFrameTimeline(vsyncEventData, frameInterval, systemTime(SYSTEM_TIME_MONOTONIC),
+ generateFrameTimeline(vsyncEventData, frameInterval.ns(), systemTime(SYSTEM_TIME_MONOTONIC),
presentTime, deadline);
return vsyncEventData;
}
@@ -549,9 +537,9 @@
connection->frameRate);
}
- return mThrottleVsyncCallback &&
- mThrottleVsyncCallback(event.vsync.vsyncData.preferredExpectedPresentationTime(),
- connection->mOwnerUid);
+ const auto expectedPresentTime =
+ TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime());
+ return mCallback.throttleVsync(expectedPresentTime, connection->mOwnerUid);
};
switch (event.header.type) {
@@ -671,9 +659,9 @@
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- const int64_t frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
- copy.vsync.vsyncData.frameInterval = frameInterval;
- generateFrameTimeline(copy.vsync.vsyncData, frameInterval, copy.header.timestamp,
+ const Period frameInterval = mCallback.getVsyncPeriod(consumer->mOwnerUid);
+ copy.vsync.vsyncData.frameInterval = frameInterval.ns();
+ generateFrameTimeline(copy.vsync.vsyncData, frameInterval.ns(), copy.header.timestamp,
event.vsync.vsyncData.preferredExpectedPresentationTime(),
event.vsync.vsyncData.preferredDeadlineTimestamp());
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 576910e..a7c8b74 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -54,7 +54,6 @@
// ---------------------------------------------------------------------------
-using ResyncCallback = std::function<void()>;
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
enum class VSyncRequest {
@@ -69,7 +68,7 @@
class EventThreadConnection : public gui::BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
+ EventThreadConnection(EventThread*, uid_t callingUid,
EventRegistrationFlags eventRegistration = {});
virtual ~EventThreadConnection();
@@ -80,9 +79,6 @@
binder::Status requestNextVsync() override; // asynchronous
binder::Status getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) override;
- // Called in response to requestNextVsync.
- const ResyncCallback resyncCallback;
-
VSyncRequest vsyncRequest = VSyncRequest::None;
const uid_t mOwnerUid;
const EventRegistrationFlags mEventRegistration;
@@ -104,7 +100,7 @@
virtual ~EventThread();
virtual sp<EventThreadConnection> createEventConnection(
- ResyncCallback, EventRegistrationFlags eventRegistration = {}) const = 0;
+ EventRegistrationFlags eventRegistration = {}) const = 0;
// Feed clients with fake VSYNC, e.g. while the display is off.
virtual void enableSyntheticVsync(bool) = 0;
@@ -136,20 +132,25 @@
virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
};
+struct IEventThreadCallback {
+ virtual ~IEventThreadCallback() = default;
+
+ virtual bool throttleVsync(TimePoint, uid_t) = 0;
+ virtual Period getVsyncPeriod(uid_t) = 0;
+ virtual void resync() = 0;
+};
+
namespace impl {
class EventThread : public android::EventThread {
public:
- using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
- using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
-
EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule>,
- frametimeline::TokenManager*, ThrottleVsyncCallback, GetVsyncPeriodFunction,
+ frametimeline::TokenManager*, IEventThreadCallback& callback,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration);
~EventThread();
sp<EventThreadConnection> createEventConnection(
- ResyncCallback, EventRegistrationFlags eventRegistration = {}) const override;
+ EventRegistrationFlags eventRegistration = {}) const override;
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
@@ -214,8 +215,7 @@
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
- const ThrottleVsyncCallback mThrottleVsyncCallback;
- const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
+ IEventThreadCallback& mCallback;
std::thread mThread;
mutable std::mutex mMutex;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index aa24f56..68e2ce9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -211,6 +211,17 @@
targeters.try_emplace(id, &targeter);
}
+ if (flagutils::vrrConfigEnabled() &&
+ CC_UNLIKELY(mPacesetterFrameDurationFractionToSkip > 0.f)) {
+ const auto period = pacesetterTargeter.target().expectedFrameDuration();
+ const auto skipDuration = Duration::fromNs(
+ static_cast<nsecs_t>(period.ns() * mPacesetterFrameDurationFractionToSkip));
+ ATRACE_FORMAT("Injecting jank for %f%% of the frame (%" PRId64 " ns)",
+ mPacesetterFrameDurationFractionToSkip * 100, skipDuration.ns());
+ std::this_thread::sleep_for(skipDuration);
+ mPacesetterFrameDurationFractionToSkip = 0.f;
+ }
+
const auto resultsPerDisplay = compositor.composite(pacesetterId, targeters);
compositor.sample();
@@ -242,36 +253,35 @@
return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), frameRate);
}
-impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
- return [this](nsecs_t expectedVsyncTime, uid_t uid) {
- return !isVsyncValid(TimePoint::fromNs(expectedVsyncTime), uid);
- };
+bool Scheduler::throttleVsync(android::TimePoint expectedPresentTime, uid_t uid) {
+ return !isVsyncValid(expectedPresentTime, uid);
}
-impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
- return [this](uid_t uid) {
- const auto [refreshRate, period] = [this] {
- std::scoped_lock lock(mDisplayLock);
- const auto pacesetterOpt = pacesetterDisplayLocked();
- LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
- const Display& pacesetter = *pacesetterOpt;
- return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
- pacesetter.schedulePtr->period());
- }();
+Period Scheduler::getVsyncPeriod(uid_t uid) {
+ const auto [refreshRate, period] = [this] {
+ std::scoped_lock lock(mDisplayLock);
+ const auto pacesetterOpt = pacesetterDisplayLocked();
+ LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
+ const Display& pacesetter = *pacesetterOpt;
+ return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
+ pacesetter.schedulePtr->period());
+ }();
- const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
+ const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
- const auto frameRate = getFrameRateOverride(uid);
- if (!frameRate.has_value()) {
- return currentPeriod.ns();
- }
+ const auto frameRate = getFrameRateOverride(uid);
+ if (!frameRate.has_value()) {
+ return currentPeriod;
+ }
- const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
- if (divisor <= 1) {
- return currentPeriod.ns();
- }
- return currentPeriod.ns() * divisor;
- };
+ const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
+ if (divisor <= 1) {
+ return currentPeriod;
+ }
+
+ // TODO(b/299378819): the casting is not needed, but we need a flag as it might change
+ // behaviour.
+ return Period::fromNs(currentPeriod.ns() * divisor);
}
ConnectionHandle Scheduler::createEventThread(Cycle cycle,
@@ -279,9 +289,7 @@
std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) {
auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
- getVsyncSchedule(), tokenManager,
- makeThrottleVsyncCallback(),
- makeGetVsyncPeriodFunction(),
+ getVsyncSchedule(), tokenManager, *this,
workDuration, readyDuration);
auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle;
@@ -293,7 +301,7 @@
const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
- auto connection = eventThread->createEventConnection([&] { resync(); });
+ auto connection = eventThread->createEventConnection();
std::lock_guard<std::mutex> lock(mConnectionsLock);
mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
@@ -307,8 +315,7 @@
std::scoped_lock lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
- return mConnections[handle].thread->createEventConnection([&] { resync(); },
- eventRegistration);
+ return mConnections[handle].thread->createEventConnection(eventRegistration);
}();
const auto layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 822f7cc..f652bb2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -98,7 +98,7 @@
class VsyncSchedule;
-class Scheduler : android::impl::MessageQueue {
+class Scheduler : public IEventThreadCallback, android::impl::MessageQueue {
using Impl = android::impl::MessageQueue;
public:
@@ -217,7 +217,6 @@
ftl::FakeGuard guard(kMainThreadContext);
resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate);
}
- void resync() EXCLUDES(mDisplayLock);
void forceNextResync() { mLastResyncTime = 0; }
// Passes a vsync sample to VsyncController. Returns true if
@@ -324,6 +323,11 @@
return mFeatures.test(Feature::kSmallDirtyContentDetection);
}
+ // Injects a delay that is a fraction of the predicted frame duration for the next frame.
+ void injectPacesetterDelay(float frameDurationFraction) REQUIRES(kMainThreadContext) {
+ mPacesetterFrameDurationFractionToSkip = frameDurationFraction;
+ }
+
private:
friend class TestableScheduler;
@@ -420,8 +424,10 @@
void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
- android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
- android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
+ // IEventThreadCallback overrides
+ bool throttleVsync(TimePoint, uid_t) override;
+ Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
+ void resync() override EXCLUDES(mDisplayLock);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -451,6 +457,9 @@
// Timer used to monitor display power mode.
ftl::Optional<OneShotTimer> mDisplayPowerTimer;
+ // Injected delay prior to compositing, for simulating jank.
+ float mPacesetterFrameDurationFractionToSkip GUARDED_BY(kMainThreadContext) = 0.f;
+
ISchedulerCallback& mSchedulerCallback;
// mDisplayLock may be locked while under mPolicyLock.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b7d2f9a..12aacad 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -6660,9 +6660,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1044 are currently used for backdoors. The code
+ // Numbers from 1000 to 1045 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1044) {
+ if (code >= 1000 && code <= 1045) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -7151,6 +7151,39 @@
}
return NO_ERROR;
}
+ // Inject jank
+ // First argument is a float that describes the fraction of frame duration to jank by.
+ // Second argument is a delay in ms for triggering the jank. This is useful for working
+ // with tools that steal the adb connection. This argument is optional.
+ case 1045: {
+ if (flagutils::vrrConfigEnabled()) {
+ float jankAmount = data.readFloat();
+ int32_t jankDelayMs = 0;
+ if (data.readInt32(&jankDelayMs) != NO_ERROR) {
+ jankDelayMs = 0;
+ }
+
+ const auto jankDelayDuration = Duration(std::chrono::milliseconds(jankDelayMs));
+
+ const bool jankAmountValid = jankAmount > 0.0 && jankAmount < 100.0;
+
+ if (!jankAmountValid) {
+ ALOGD("Ignoring invalid jank amount: %f", jankAmount);
+ reply->writeInt32(BAD_VALUE);
+ return BAD_VALUE;
+ }
+
+ (void)mScheduler->scheduleDelayed(
+ [&, jankAmount]() FTL_FAKE_GUARD(kMainThreadContext) {
+ mScheduler->injectPacesetterDelay(jankAmount);
+ scheduleComposite(FrameHint::kActive);
+ },
+ jankDelayDuration.ns());
+ reply->writeInt32(NO_ERROR);
+ return NO_ERROR;
+ }
+ return err;
+ }
}
}
return err;
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 5512734..6f53d62 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -2,6 +2,9 @@
"imports": [
{
"path": "frameworks/native/libs/gui"
+ },
+ {
+ "path": "frameworks/native/services/inputflinger"
}
],
"presubmit": [
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 7062a4e..effbfdb 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -56,31 +56,35 @@
ATRACE_NAME("WindowInfosListenerInvoker::removeWindowInfosListener");
sp<IBinder> asBinder = IInterface::asBinder(listener);
asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
- mWindowInfosListeners.erase(asBinder);
+ eraseListenerAndAckMessages(asBinder);
}});
}
void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) {
BackgroundExecutor::getInstance().sendCallbacks({[this, who]() {
ATRACE_NAME("WindowInfosListenerInvoker::binderDied");
- auto it = mWindowInfosListeners.find(who);
- int64_t listenerId = it->second.first;
- mWindowInfosListeners.erase(who);
-
- std::vector<int64_t> vsyncIds;
- for (auto& [vsyncId, state] : mUnackedState) {
- if (std::find(state.unackedListenerIds.begin(), state.unackedListenerIds.end(),
- listenerId) != state.unackedListenerIds.end()) {
- vsyncIds.push_back(vsyncId);
- }
- }
-
- for (int64_t vsyncId : vsyncIds) {
- ackWindowInfosReceived(vsyncId, listenerId);
- }
+ eraseListenerAndAckMessages(who);
}});
}
+void WindowInfosListenerInvoker::eraseListenerAndAckMessages(const wp<IBinder>& binder) {
+ auto it = mWindowInfosListeners.find(binder);
+ int64_t listenerId = it->second.first;
+ mWindowInfosListeners.erase(binder);
+
+ std::vector<int64_t> vsyncIds;
+ for (auto& [vsyncId, state] : mUnackedState) {
+ if (std::find(state.unackedListenerIds.begin(), state.unackedListenerIds.end(),
+ listenerId) != state.unackedListenerIds.end()) {
+ vsyncIds.push_back(vsyncId);
+ }
+ }
+
+ for (int64_t vsyncId : vsyncIds) {
+ ackWindowInfosReceived(vsyncId, listenerId);
+ }
+}
+
void WindowInfosListenerInvoker::windowInfosChanged(
gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
bool forceImmediateCall) {
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index f36b0ed..261fd0f 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -67,6 +67,7 @@
std::optional<gui::WindowInfosUpdate> mDelayedUpdate;
WindowInfosReportedListenerSet mReportedListeners;
+ void eraseListenerAndAckMessages(const wp<IBinder>&);
struct UnackedState {
ftl::SmallVector<int64_t, kStaticCapacity> unackedListenerIds;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index cbbfa16..2f5a360 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -97,21 +97,26 @@
return displayId;
}
+struct EventThreadCallback : public IEventThreadCallback {
+ bool throttleVsync(TimePoint, uid_t) override { return false; }
+ Period getVsyncPeriod(uid_t) override { return kSyncPeriod; }
+ void resync() override {}
+};
+
void SchedulerFuzzer::fuzzEventThread() {
mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>(
new scheduler::VsyncSchedule(getPhysicalDisplayId(),
std::make_shared<mock::VSyncTracker>(),
std::make_shared<mock::VSyncDispatch>(), nullptr));
- const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
+ EventThreadCallback callback;
std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
- android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, nullptr, getVsyncPeriod,
+ android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, callback,
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
sp<EventThreadConnection> connection =
- sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>(),
- nullptr);
+ sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>());
thread->requestNextVsync(connection);
thread->setVsyncRate(mFdp.ConsumeIntegral<uint32_t>() /*rate*/, connection);
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index bfc03aa..d4ab786 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -11,8 +11,8 @@
flag {
name: "connected_display"
namespace: "core_graphics"
- description: "Controls SurfaceFlinger support for Connected Displays"
- bug: "278199093"
+ description: "Controls SurfaceFlinger support for Connected Displays in 24Q1"
+ bug: "299486625"
is_fixed_read_only: true
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index e32cf88..fa31643 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -67,15 +67,13 @@
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(mEventThread,
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ .WillOnce(Return(
+ sp<EventThreadConnection>::make(mEventThread, mock::EventThread::kCallingUid)));
EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(mSFEventThread,
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(),
std::make_shared<mock::VSyncTracker>(),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 9b46009..8891c06 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -57,7 +57,7 @@
} // namespace
-class EventThreadTest : public testing::Test {
+class EventThreadTest : public testing::Test, public IEventThreadCallback {
protected:
static constexpr std::chrono::nanoseconds kWorkDuration = 0ms;
static constexpr std::chrono::nanoseconds kReadyDuration = 3ms;
@@ -65,10 +65,8 @@
class MockEventThreadConnection : public EventThreadConnection {
public:
MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
- ResyncCallback&& resyncCallback,
EventRegistrationFlags eventRegistration)
- : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
- eventRegistration) {}
+ : EventThreadConnection(eventThread, callingUid, eventRegistration) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -78,7 +76,14 @@
EventThreadTest();
~EventThreadTest() override;
- void setupEventThread(std::chrono::nanoseconds vsyncPeriod);
+ void SetUp() override { mVsyncPeriod = VSYNC_PERIOD; }
+
+ // IEventThreadCallback overrides
+ bool throttleVsync(TimePoint, uid_t) override;
+ Period getVsyncPeriod(uid_t) override;
+ void resync() override;
+
+ void setupEventThread();
sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
EventRegistrationFlags eventRegistration = {},
uid_t ownerUid = mConnectionUid);
@@ -92,8 +97,7 @@
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventFrameTimelinesCorrect(
nsecs_t expectedTimestamp, gui::VsyncEventData::FrameTimeline preferredVsyncData);
- void expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData,
- std::chrono::nanoseconds vsyncPeriod);
+ void expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData);
void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -133,6 +137,8 @@
sp<MockEventThreadConnection> mThrottledConnection;
std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
+ std::chrono::nanoseconds mVsyncPeriod;
+
static constexpr uid_t mConnectionUid = 443;
static constexpr uid_t mThrottledConnectionUid = 177;
};
@@ -168,17 +174,24 @@
EXPECT_TRUE(mVSyncCallbackUnregisterRecorder.waitForCall().has_value());
}
-void EventThreadTest::setupEventThread(std::chrono::nanoseconds vsyncPeriod) {
- const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
- mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
- return (uid == mThrottledConnectionUid);
- };
- const auto getVsyncPeriod = [vsyncPeriod](uid_t uid) { return vsyncPeriod.count(); };
+bool EventThreadTest::throttleVsync(android::TimePoint expectedVsyncTimestamp, uid_t uid) {
+ mThrottleVsyncCallRecorder.recordCall(expectedVsyncTimestamp.ns(), uid);
+ return (uid == mThrottledConnectionUid);
+}
+Period EventThreadTest::getVsyncPeriod(uid_t) {
+ return mVsyncPeriod;
+}
+
+void EventThreadTest::resync() {
+ mResyncCallRecorder.recordCall();
+}
+
+void EventThreadTest::setupEventThread() {
mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule,
- mTokenManager.get(), throttleVsync,
- getVsyncPeriod, kWorkDuration, kReadyDuration);
+ mTokenManager.get(), *this, kWorkDuration,
+ kReadyDuration);
// EventThread should register itself as VSyncSource callback.
EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value());
@@ -200,9 +213,7 @@
ConnectionEventRecorder& recorder, EventRegistrationFlags eventRegistration,
uid_t ownerUid) {
sp<MockEventThreadConnection> connection =
- sp<MockEventThreadConnection>::make(mThread.get(), ownerUid,
- mResyncCallRecorder.getInvocable(),
- eventRegistration);
+ sp<MockEventThreadConnection>::make(mThread.get(), ownerUid, eventRegistration);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -292,10 +303,9 @@
}
}
-void EventThreadTest::expectVsyncEventDataFrameTimelinesValidLength(
- VsyncEventData vsyncEventData, std::chrono::nanoseconds vsyncPeriod) {
+void EventThreadTest::expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData) {
float nonPreferredTimelinesAmount =
- scheduler::VsyncConfig::kEarlyLatchMaxThreshold / vsyncPeriod;
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold / mVsyncPeriod;
EXPECT_LE(vsyncEventData.frameTimelinesLength, nonPreferredTimelinesAmount + 1)
<< "Amount of non-preferred frame timelines too many;"
<< " expected presentation time will be over threshold";
@@ -357,7 +367,7 @@
*/
TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
EXPECT_FALSE(mVSyncCallbackRegisterRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncCallbackScheduleRecorder.waitForCall(0us).has_value());
@@ -368,7 +378,7 @@
}
TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
@@ -381,7 +391,7 @@
}
TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -414,7 +424,7 @@
}
TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -428,12 +438,12 @@
}
TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesValidLength) {
+ setupEventThread();
// The VsyncEventData should not have kFrameTimelinesCapacity amount of valid frame timelines,
// due to longer vsync period and kEarlyLatchMaxThreshold. Use length-2 to avoid decimal
// truncation (e.g. 60Hz has 16.6... ms vsync period).
- std::chrono::nanoseconds vsyncPeriod(scheduler::VsyncConfig::kEarlyLatchMaxThreshold /
- (VsyncEventData::kFrameTimelinesCapacity - 2));
- setupEventThread(vsyncPeriod);
+ mVsyncPeriod = (scheduler::VsyncConfig::kEarlyLatchMaxThreshold /
+ (VsyncEventData::kFrameTimelinesCapacity - 2));
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -449,11 +459,11 @@
ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
<< expectedTimestamp;
const VsyncEventData vsyncEventData = std::get<0>(args.value()).vsync.vsyncData;
- expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, vsyncPeriod);
+ expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData);
}
TEST_F(EventThreadTest, getLatestVsyncEventData) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const nsecs_t now = systemTime();
const nsecs_t preferredExpectedPresentationTime = now + 20000000;
@@ -469,7 +479,7 @@
// Check EventThread immediately requested a resync.
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
- expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, VSYNC_PERIOD);
+ expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData);
EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
<< "Deadline timestamp should be greater than frame time";
for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
@@ -508,7 +518,7 @@
}
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
@@ -537,7 +547,7 @@
}
TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->setVsyncRate(1, mConnection);
@@ -562,7 +572,7 @@
}
TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->setVsyncRate(2, mConnection);
@@ -590,7 +600,7 @@
}
TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->setVsyncRate(1, mConnection);
@@ -609,7 +619,7 @@
}
TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
@@ -635,7 +645,7 @@
}
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
@@ -661,42 +671,42 @@
}
TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->setDuration(321ns, 456ns);
expectVSyncSetDurationCallReceived(321ns, 456ns);
}
TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugInternalConnect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugExternalConnect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, true);
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postConfigChangedPrimary) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(INTERNAL_DISPLAY_ID)
@@ -710,7 +720,7 @@
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(EXTERNAL_DISPLAY_ID)
@@ -724,7 +734,7 @@
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(DISPLAY_ID_64BIT)
@@ -737,7 +747,7 @@
}
TEST_F(EventThreadTest, suppressConfigChanged) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
ConnectionEventRecorder suppressConnectionEventRecorder{0};
sp<MockEventThreadConnection> suppressConnection =
@@ -758,7 +768,7 @@
}
TEST_F(EventThreadTest, postUidFrameRateMapping) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const std::vector<FrameRateOverride> overrides = {
{.uid = 1, .frameRateHz = 20},
@@ -771,7 +781,7 @@
}
TEST_F(EventThreadTest, suppressUidFrameRateMapping) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const std::vector<FrameRateOverride> overrides = {
{.uid = 1, .frameRateHz = 20},
@@ -791,7 +801,7 @@
}
TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
// Signal that we want the next vsync event to be posted to the throttled connection
mThread->requestNextVsync(mThrottledConnection);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 3200003..173f941 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -55,8 +55,7 @@
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, /*callingUid*/ static_cast<uid_t>(0),
- ResyncCallback()) {}
+ : EventThreadConnection(eventThread, /*callingUid*/ static_cast<uid_t>(0)) {}
~MockEventThreadConnection() = default;
MOCK_METHOD1(stealReceiveChannel, binder::Status(gui::BitTube* outChannel));
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 3a5fdf9..aeac80d 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -124,14 +124,12 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 908c9ab..dd998ba 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -281,14 +281,12 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock);
auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock);
diff --git a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
index c7b845e..cfb047c 100644
--- a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
@@ -245,4 +245,42 @@
EXPECT_EQ(callCount, 1);
}
+// Test that WindowInfosListenerInvoker#removeWindowInfosListener acks any unacked messages for
+// the removed listener.
+TEST_F(WindowInfosListenerInvokerTest, removeListenerAcks) {
+ // Don't ack in this listener to ensure there's an unacked message when the listener is later
+ // removed.
+ gui::WindowInfosListenerInfo listenerToBeRemovedInfo;
+ auto listenerToBeRemoved = sp<Listener>::make([](const gui::WindowInfosUpdate&) {});
+ mInvoker->addWindowInfosListener(listenerToBeRemoved, &listenerToBeRemovedInfo);
+
+ std::mutex mutex;
+ std::condition_variable cv;
+ int callCount = 0;
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate& update) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+ listenerInfo.windowInfosPublisher
+ ->ackWindowInfosReceived(update.vsyncId,
+ listenerInfo.listenerId);
+ }),
+ &listenerInfo);
+
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[&]() { mInvoker->windowInfosChanged({}, {}, false); }});
+ mInvoker->removeWindowInfosListener(listenerToBeRemoved);
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[&]() { mInvoker->windowInfosChanged({}, {}, false); }});
+
+ // Verify that the second listener is called twice. If unacked messages aren't removed when the
+ // first listener is removed, this will fail.
+ {
+ std::unique_lock lock{mutex};
+ cv.wait(lock, [&]() { return callCount == 2; });
+ }
+ EXPECT_EQ(callCount, 2);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 8e782eb..866af3b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -29,8 +29,16 @@
EventThread();
~EventThread() override;
- MOCK_METHOD(sp<EventThreadConnection>, createEventConnection,
- (ResyncCallback, EventRegistrationFlags), (const, override));
+ // TODO(b/302035909): workaround otherwise gtest complains about
+ // error: no viable conversion from
+ // 'tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration> &&>' to 'const
+ // tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration>>'
+ sp<EventThreadConnection> createEventConnection(EventRegistrationFlags flags) const override {
+ return createEventConnection(false, flags);
+ }
+ MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (bool, EventRegistrationFlags),
+ (const));
+
MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index c5870d4..dcef54d 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -944,6 +944,11 @@
return VK_ERROR_SURFACE_LOST_KHR;
}
+ // Additional buffer count over min_undequeued_buffers in vulkan came from 2 total
+ // being technically enough for fifo (although a poor experience) vs 3 being the
+ // absolute minimum for mailbox to be useful. So min_undequeued_buffers + 2 is sensible
+ static constexpr int default_additional_buffers = 2;
+
if(pPresentMode != nullptr) {
switch (pPresentMode->presentMode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR:
@@ -951,8 +956,8 @@
break;
case VK_PRESENT_MODE_MAILBOX_KHR:
case VK_PRESENT_MODE_FIFO_KHR:
- capabilities->minImageCount =
- std::min(max_buffer_count, min_undequeued_buffers + 2);
+ capabilities->minImageCount = std::min(max_buffer_count,
+ min_undequeued_buffers + default_additional_buffers);
capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
break;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
@@ -971,7 +976,8 @@
break;
}
} else {
- capabilities->minImageCount = std::min(max_buffer_count, min_undequeued_buffers + 2);
+ capabilities->minImageCount = std::min(max_buffer_count,
+ min_undequeued_buffers + default_additional_buffers);
capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
}
}