Merge "Fix flattenLayers_ActiveLayersWithLowFpsAreFlattened" into main
diff --git a/Android.bp b/Android.bp
index 7f1ef67..8c4dfbb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -97,6 +97,12 @@
],
}
+aidl_library {
+ name: "PersistableBundle_aidl",
+ hdrs: ["aidl/binder/android/os/PersistableBundle.aidl"],
+ strip_import_prefix: "aidl/binder",
+}
+
cc_library_headers {
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl
index 493ecb4..248e973 100644
--- a/aidl/binder/android/os/PersistableBundle.aidl
+++ b/aidl/binder/android/os/PersistableBundle.aidl
@@ -17,4 +17,4 @@
package android.os;
-@JavaOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h" ndk_header "android/persistable_bundle_aidl.h";
diff --git a/cmds/evemu-record/Android.bp b/cmds/evemu-record/Android.bp
new file mode 100644
index 0000000..1edacec
--- /dev/null
+++ b/cmds/evemu-record/Android.bp
@@ -0,0 +1,13 @@
+package {
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+rust_binary {
+ name: "evemu-record",
+ srcs: ["main.rs"],
+ rustlibs: [
+ "libclap",
+ "liblibc",
+ "libnix",
+ ],
+}
diff --git a/cmds/evemu-record/OWNERS b/cmds/evemu-record/OWNERS
new file mode 100644
index 0000000..c88bfe9
--- /dev/null
+++ b/cmds/evemu-record/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/cmds/evemu-record/evdev.rs b/cmds/evemu-record/evdev.rs
new file mode 100644
index 0000000..35feea1
--- /dev/null
+++ b/cmds/evemu-record/evdev.rs
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Wrappers for the Linux evdev APIs.
+
+use std::fs::File;
+use std::io;
+use std::mem;
+use std::os::fd::{AsRawFd, OwnedFd};
+use std::path::Path;
+
+use libc::c_int;
+use nix::sys::time::TimeVal;
+
+pub const SYN_CNT: usize = 0x10;
+pub const KEY_CNT: usize = 0x300;
+pub const REL_CNT: usize = 0x10;
+pub const ABS_CNT: usize = 0x40;
+pub const MSC_CNT: usize = 0x08;
+pub const SW_CNT: usize = 0x11;
+pub const LED_CNT: usize = 0x10;
+pub const SND_CNT: usize = 0x08;
+pub const REP_CNT: usize = 0x02;
+
+// Disable naming warnings, as these are supposed to match the EV_ constants in input-event-codes.h.
+#[allow(non_camel_case_types)]
+// Some of these types aren't referenced for evemu purposes, but are included for completeness.
+#[allow(dead_code)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[repr(u16)]
+pub enum EventType {
+ SYN = 0x00,
+ KEY = 0x01,
+ REL = 0x02,
+ ABS = 0x03,
+ MSC = 0x04,
+ SW = 0x05,
+ LED = 0x11,
+ SND = 0x12,
+ REP = 0x14,
+ FF = 0x15,
+ PWR = 0x16,
+ FF_STATUS = 0x17,
+}
+
+impl EventType {
+ fn code_count(&self) -> usize {
+ match self {
+ EventType::SYN => SYN_CNT,
+ EventType::KEY => KEY_CNT,
+ EventType::REL => REL_CNT,
+ EventType::ABS => ABS_CNT,
+ EventType::MSC => MSC_CNT,
+ EventType::SW => SW_CNT,
+ EventType::LED => LED_CNT,
+ EventType::SND => SND_CNT,
+ EventType::REP => REP_CNT,
+ _ => {
+ panic!("Event type {self:?} does not have a defined code count.");
+ }
+ }
+ }
+}
+
+pub const EVENT_TYPES_WITH_BITMAPS: [EventType; 7] = [
+ EventType::KEY,
+ EventType::REL,
+ EventType::ABS,
+ EventType::MSC,
+ EventType::SW,
+ EventType::LED,
+ EventType::SND,
+];
+
+const INPUT_PROP_CNT: usize = 32;
+
+/// The `ioctl_*!` macros create public functions by default, so this module makes them private.
+mod ioctl {
+ use nix::{ioctl_read, ioctl_read_buf};
+
+ ioctl_read!(eviocgid, b'E', 0x02, super::DeviceId);
+ ioctl_read_buf!(eviocgname, b'E', 0x06, u8);
+ ioctl_read_buf!(eviocgprop, b'E', 0x09, u8);
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct DeviceId {
+ pub bus_type: u16,
+ pub vendor: u16,
+ pub product: u16,
+ pub version: u16,
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct AbsoluteAxisInfo {
+ pub value: i32,
+ pub minimum: i32,
+ pub maximum: i32,
+ pub fuzz: i32,
+ pub flat: i32,
+ pub resolution: i32,
+}
+
+#[repr(C)]
+pub struct InputEvent {
+ pub time: TimeVal,
+ pub type_: u16,
+ pub code: u16,
+ pub value: i32,
+}
+
+impl InputEvent {
+ pub fn offset_time_by(&self, offset: TimeVal) -> InputEvent {
+ InputEvent { time: self.time - offset, ..*self }
+ }
+}
+
+impl Default for InputEvent {
+ fn default() -> Self {
+ InputEvent { time: TimeVal::new(0, 0), type_: 0, code: 0, value: 0 }
+ }
+}
+
+/// An object representing an input device using Linux's evdev protocol.
+pub struct Device {
+ fd: OwnedFd,
+}
+
+/// # Safety
+///
+/// `ioctl` must be safe to call with the given file descriptor and a pointer to a buffer of
+/// `initial_buf_size` `u8`s.
+unsafe fn buf_from_ioctl(
+ ioctl: unsafe fn(c_int, &mut [u8]) -> nix::Result<c_int>,
+ fd: &OwnedFd,
+ initial_buf_size: usize,
+) -> Result<Vec<u8>, nix::errno::Errno> {
+ let mut buf = vec![0; initial_buf_size];
+ // SAFETY:
+ // Here we're relying on the safety guarantees for `ioctl` made by the caller.
+ match unsafe { ioctl(fd.as_raw_fd(), buf.as_mut_slice()) } {
+ Ok(len) if len < 0 => {
+ panic!("ioctl returned invalid length {len}");
+ }
+ Ok(len) => {
+ buf.truncate(len as usize);
+ Ok(buf)
+ }
+ Err(err) => Err(err),
+ }
+}
+
+impl Device {
+ /// Opens a device from the evdev node at the given path.
+ pub fn open(path: &Path) -> io::Result<Device> {
+ Ok(Device { fd: OwnedFd::from(File::open(path)?) })
+ }
+
+ /// Returns the name of the device, as set by the relevant kernel driver.
+ pub fn name(&self) -> Result<String, nix::errno::Errno> {
+ // There's no official maximum length for evdev device names. The Linux HID driver
+ // currently supports names of at most 151 bytes (128 from the device plus a suffix of up
+ // to 23 bytes). 256 seems to be the buffer size most commonly used in evdev bindings, so
+ // we use it here.
+ //
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // The ioctl_read_buf macro prevents the retrieved data from overflowing the buffer created
+ // by buf_from_ioctl by passing in the size to the ioctl, meaning that the kernel's
+ // str_to_user function will truncate the string to that length.
+ let mut buf = unsafe { buf_from_ioctl(ioctl::eviocgname, &self.fd, 256)? };
+ assert!(!buf.is_empty(), "buf is too short for an empty null-terminated string");
+ assert_eq!(buf.pop().unwrap(), 0, "buf is not a null-terminated string");
+ Ok(String::from_utf8_lossy(buf.as_slice()).into_owned())
+ }
+
+ pub fn ids(&self) -> Result<DeviceId, nix::errno::Errno> {
+ let mut ids = DeviceId::default();
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // We know that the pointer to ids is valid because we just allocated it.
+ unsafe { ioctl::eviocgid(self.fd.as_raw_fd(), &mut ids) }.map(|_| ids)
+ }
+
+ pub fn properties_bitmap(&self) -> Result<Vec<u8>, nix::errno::Errno> {
+ let buf_size = (INPUT_PROP_CNT + 7) / 8;
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // The ioctl_read_buf macro prevents the retrieved data from overflowing the buffer created
+ // by buf_from_ioctl by passing in the size to the ioctl, meaning that the kernel's
+ // str_to_user function will truncate the string to that length.
+ unsafe { buf_from_ioctl(ioctl::eviocgprop, &self.fd, buf_size) }
+ }
+
+ pub fn bitmap_for_event_type(&self, event_type: EventType) -> nix::Result<Vec<u8>> {
+ let buf_size = (event_type.code_count() + 7) / 8;
+ let mut buf = vec![0; buf_size];
+
+ // The EVIOCGBIT ioctl can't be bound using ioctl_read_buf! like the others, since it uses
+ // part of its ioctl code as an additional parameter, for the event type. Hence this unsafe
+ // block is a manual expansion of ioctl_read_buf!.
+ //
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // We prevent the retrieved data from overflowing buf by passing in the size of buf to the
+ // ioctl, meaning that the kernel's str_to_user function will truncate the string to that
+ // length. We also panic if the ioctl returns a length longer than buf, hopefully before the
+ // overflow can do any damage.
+ match nix::errno::Errno::result(unsafe {
+ libc::ioctl(
+ self.fd.as_raw_fd(),
+ nix::request_code_read!(b'E', 0x20 + event_type as u16, buf.len())
+ as nix::sys::ioctl::ioctl_num_type,
+ buf.as_mut_ptr(),
+ )
+ }) {
+ Ok(len) if len < 0 => {
+ panic!("EVIOCGBIT returned invalid length {len} for event type {event_type:?}");
+ }
+ Ok(len) => {
+ buf.truncate(len as usize);
+ Ok(buf)
+ }
+ Err(err) => Err(err),
+ }
+ }
+
+ pub fn supported_axes_of_type(&self, event_type: EventType) -> nix::Result<Vec<u16>> {
+ let mut axes = Vec::new();
+ for (i, byte_ref) in self.bitmap_for_event_type(event_type)?.iter().enumerate() {
+ let mut byte = *byte_ref;
+ for j in 0..8 {
+ if byte & 1 == 1 {
+ axes.push((i * 8 + j) as u16);
+ }
+ byte >>= 1;
+ }
+ }
+ Ok(axes)
+ }
+
+ pub fn absolute_axis_info(&self, axis: u16) -> nix::Result<AbsoluteAxisInfo> {
+ let mut info = AbsoluteAxisInfo::default();
+ // The EVIOCGABS ioctl can't be bound using ioctl_read! since it uses part of its ioctl code
+ // as an additional parameter, for the axis code. Hence this unsafe block is a manual
+ // expansion of ioctl_read!.
+ //
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // We know that the pointer to info is valid because we just allocated it.
+ nix::errno::Errno::result(unsafe {
+ nix::libc::ioctl(
+ self.fd.as_raw_fd(),
+ nix::request_code_read!(b'E', 0x40 + axis, mem::size_of::<AbsoluteAxisInfo>()),
+ &mut info,
+ )
+ })
+ .map(|_| info)
+ }
+
+ pub fn read_event(&self) -> nix::Result<InputEvent> {
+ let mut event = InputEvent::default();
+
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // We know that the pointer to event is valid because we just allocated it, and that the
+ // data structures match up because InputEvent is repr(C) and all its members are repr(C)
+ // or primitives that support all representations without niches.
+ nix::errno::Errno::result(unsafe {
+ libc::read(
+ self.fd.as_raw_fd(),
+ &mut event as *mut _ as *mut std::ffi::c_void,
+ mem::size_of::<InputEvent>(),
+ )
+ })
+ .map(|_| event)
+ }
+}
diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs
new file mode 100644
index 0000000..6f5deb9
--- /dev/null
+++ b/cmds/evemu-record/main.rs
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! A Rust implementation of the evemu-record command from the [FreeDesktop evemu suite][evemu] of
+//! tools.
+//!
+//! [evemu]: https://gitlab.freedesktop.org/libevdev/evemu
+
+use std::cmp;
+use std::error::Error;
+use std::fs;
+use std::io;
+use std::io::{BufRead, Write};
+use std::path::PathBuf;
+
+use clap::Parser;
+use nix::sys::time::TimeVal;
+
+mod evdev;
+
+/// Records evdev events from an input device in a format compatible with the FreeDesktop evemu
+/// library.
+#[derive(Parser, Debug)]
+struct Args {
+ /// The path to the input device to record. If omitted, offers a list of devices to choose from.
+ device: Option<PathBuf>,
+ /// The file to save the recording to. Defaults to standard output.
+ output_file: Option<PathBuf>,
+}
+
+fn get_choice(max: u32) -> u32 {
+ fn read_u32() -> Result<u32, std::num::ParseIntError> {
+ io::stdin().lock().lines().next().unwrap().unwrap().parse::<u32>()
+ }
+ let mut choice = read_u32();
+ while choice.is_err() || choice.clone().unwrap() > max {
+ eprint!("Enter a number between 0 and {max} inclusive: ");
+ choice = read_u32();
+ }
+ choice.unwrap()
+}
+
+fn pick_input_device() -> Result<PathBuf, io::Error> {
+ eprintln!("Available devices:");
+ let mut entries =
+ fs::read_dir("/dev/input")?.filter_map(|entry| entry.ok()).collect::<Vec<_>>();
+ entries.sort_by_key(|entry| entry.path());
+ let mut highest_number = 0;
+ for entry in entries {
+ let path = entry.path();
+ let file_name = path.file_name().unwrap().to_str().unwrap();
+ if path.is_dir() || !file_name.starts_with("event") {
+ continue;
+ }
+ let number = file_name.strip_prefix("event").unwrap().parse::<u32>();
+ if number.is_err() {
+ continue;
+ }
+ let number = number.unwrap();
+ match evdev::Device::open(path.as_path()) {
+ Ok(dev) => {
+ highest_number = cmp::max(highest_number, number);
+ eprintln!(
+ "{}:\t{}",
+ path.display(),
+ dev.name().unwrap_or("[could not read name]".to_string()),
+ );
+ }
+ Err(_) => {
+ eprintln!("Couldn't open {}", path.display());
+ }
+ }
+ }
+ eprint!("Select the device event number [0-{highest_number}]: ");
+ let choice = get_choice(highest_number);
+ Ok(PathBuf::from(format!("/dev/input/event{choice}")))
+}
+
+fn print_device_description(
+ device: &evdev::Device,
+ output: &mut impl Write,
+) -> Result<(), Box<dyn Error>> {
+ // TODO(b/302297266): report LED and SW states, then bump the version to EVEMU 1.3.
+ writeln!(output, "# EVEMU 1.2")?;
+ writeln!(output, "N: {}", device.name()?)?;
+
+ let ids = device.ids()?;
+ writeln!(
+ output,
+ "I: {:04x} {:04x} {:04x} {:04x}",
+ ids.bus_type, ids.vendor, ids.product, ids.version,
+ )?;
+
+ fn print_in_8_byte_chunks(
+ output: &mut impl Write,
+ prefix: &str,
+ data: &Vec<u8>,
+ ) -> Result<(), io::Error> {
+ for (i, byte) in data.iter().enumerate() {
+ if i % 8 == 0 {
+ write!(output, "{prefix}")?;
+ }
+ write!(output, " {:02x}", byte)?;
+ if (i + 1) % 8 == 0 {
+ writeln!(output)?;
+ }
+ }
+ if data.len() % 8 != 0 {
+ for _ in (data.len() % 8)..8 {
+ write!(output, " 00")?;
+ }
+ writeln!(output)?;
+ }
+ Ok(())
+ }
+
+ let props = device.properties_bitmap()?;
+ print_in_8_byte_chunks(output, "P:", &props)?;
+
+ // The SYN event type can't be queried through the EVIOCGBIT ioctl, so just hard-code it to
+ // SYN_REPORT, SYN_CONFIG, and SYN_DROPPED.
+ writeln!(output, "B: 00 0b 00 00 00 00 00 00 00")?;
+ for event_type in evdev::EVENT_TYPES_WITH_BITMAPS {
+ let bits = device.bitmap_for_event_type(event_type)?;
+ print_in_8_byte_chunks(output, format!("B: {:02x}", event_type as u16).as_str(), &bits)?;
+ }
+
+ for axis in device.supported_axes_of_type(evdev::EventType::ABS)? {
+ let info = device.absolute_axis_info(axis)?;
+ writeln!(
+ output,
+ "A: {axis:02x} {} {} {} {} {}",
+ info.minimum, info.maximum, info.fuzz, info.flat, info.resolution
+ )?;
+ }
+ Ok(())
+}
+
+fn print_events(device: &evdev::Device, output: &mut impl Write) -> Result<(), Box<dyn Error>> {
+ fn print_event(output: &mut impl Write, event: &evdev::InputEvent) -> Result<(), io::Error> {
+ // TODO(b/302297266): Translate events into human-readable names and add those as comments.
+ writeln!(
+ output,
+ "E: {}.{:06} {:04x} {:04x} {:04}",
+ event.time.tv_sec(),
+ event.time.tv_usec(),
+ event.type_,
+ event.code,
+ event.value,
+ )?;
+ Ok(())
+ }
+ let event = device.read_event()?;
+ // Due to a bug in the C implementation of evemu-play [0] that has since become part of the API,
+ // the timestamp of the first event in a recording shouldn't be exactly 0.0 seconds, so offset
+ // it by 1µs.
+ //
+ // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d
+ let start_time = event.time - TimeVal::new(0, 1);
+ print_event(output, &event.offset_time_by(start_time))?;
+ loop {
+ let event = device.read_event()?;
+ print_event(output, &event.offset_time_by(start_time))?;
+ }
+}
+
+fn main() -> Result<(), Box<dyn Error>> {
+ let args = Args::parse();
+
+ let device_path = args.device.unwrap_or_else(|| pick_input_device().unwrap());
+
+ let device = evdev::Device::open(device_path.as_path())?;
+ let mut output = match args.output_file {
+ Some(path) => Box::new(fs::File::create(path)?) as Box<dyn Write>,
+ None => Box::new(io::stdout().lock()),
+ };
+ print_device_description(&device, &mut output)?;
+ print_events(&device, &mut output)?;
+ Ok(())
+}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 550b374..89736ec 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -371,6 +371,12 @@
}
prebuilt_etc {
+ name: "android.software.opengles.deqp.level-2024-03-01.prebuilt.xml",
+ src: "android.software.opengles.deqp.level-2024-03-01.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.software.opengles.deqp.level-latest.prebuilt.xml",
src: "android.software.opengles.deqp.level-latest.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -407,6 +413,12 @@
}
prebuilt_etc {
+ name: "android.software.vulkan.deqp.level-2024-03-01.prebuilt.xml",
+ src: "android.software.vulkan.deqp.level-2024-03-01.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.software.vulkan.deqp.level-latest.prebuilt.xml",
src: "android.software.vulkan.deqp.level-latest.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.software.opengles.deqp.level-2024-03-01.xml b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml
new file mode 100644
index 0000000..4eeed2a
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+ dEQP tests associated with date 2023-03-01 (0x07E70301). -->
+<permissions>
+ <feature name="android.software.opengles.deqp.level" version="132645633" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml
new file mode 100644
index 0000000..8b2b4c8
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
+ tests associated with date 2023-03-01 (0x07E70301). -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132645633" />
+</permissions>
diff --git a/include/input/Input.h b/include/input/Input.h
index bd544b5..1c4ea6b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -1138,6 +1138,24 @@
std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool;
};
+/**
+ * An input event factory implementation that simply creates the input events on the heap, when
+ * needed. The caller is responsible for destroying the returned references.
+ * It is recommended that the caller wrap these return values into std::unique_ptr.
+ */
+class DynamicInputEventFactory : public InputEventFactoryInterface {
+public:
+ explicit DynamicInputEventFactory(){};
+ ~DynamicInputEventFactory(){};
+
+ KeyEvent* createKeyEvent() override { return new KeyEvent(); };
+ MotionEvent* createMotionEvent() override { return new MotionEvent(); };
+ FocusEvent* createFocusEvent() override { return new FocusEvent(); };
+ CaptureEvent* createCaptureEvent() override { return new CaptureEvent(); };
+ DragEvent* createDragEvent() override { return new DragEvent(); };
+ TouchModeEvent* createTouchModeEvent() override { return new TouchModeEvent(); };
+};
+
/*
* Describes a unique request to enable or disable Pointer Capture.
*/
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 9c0c10e..2d23b97 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -160,4 +160,90 @@
std::vector<PointerBuilder> mPointers;
};
+class KeyEventBuilder {
+public:
+ KeyEventBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mDownTime = mEventTime;
+ }
+
+ KeyEventBuilder(const KeyEvent& event) {
+ mAction = event.getAction();
+ mDeviceId = event.getDeviceId();
+ mSource = event.getSource();
+ mDownTime = event.getDownTime();
+ mEventTime = event.getEventTime();
+ mDisplayId = event.getDisplayId();
+ mFlags = event.getFlags();
+ mKeyCode = event.getKeyCode();
+ mScanCode = event.getScanCode();
+ mMetaState = event.getMetaState();
+ mRepeatCount = event.getRepeatCount();
+ }
+
+ KeyEventBuilder& deviceId(int32_t deviceId) {
+ mDeviceId = deviceId;
+ return *this;
+ }
+
+ KeyEventBuilder& downTime(nsecs_t downTime) {
+ mDownTime = downTime;
+ return *this;
+ }
+
+ KeyEventBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ KeyEventBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ KeyEventBuilder& policyFlags(int32_t policyFlags) {
+ mPolicyFlags = policyFlags;
+ return *this;
+ }
+
+ KeyEventBuilder& addFlag(uint32_t flags) {
+ mFlags |= flags;
+ return *this;
+ }
+
+ KeyEventBuilder& keyCode(int32_t keyCode) {
+ mKeyCode = keyCode;
+ return *this;
+ }
+
+ KeyEventBuilder& repeatCount(int32_t repeatCount) {
+ mRepeatCount = repeatCount;
+ return *this;
+ }
+
+ KeyEvent build() const {
+ KeyEvent event{};
+ event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
+ mAction, mFlags, mKeyCode, mScanCode, mMetaState, mRepeatCount, mDownTime,
+ mEventTime);
+ return event;
+ }
+
+private:
+ int32_t mAction;
+ int32_t mDeviceId = DEFAULT_DEVICE_ID;
+ uint32_t mSource;
+ nsecs_t mDownTime;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
+ int32_t mFlags{0};
+ int32_t mKeyCode{AKEYCODE_UNKNOWN};
+ int32_t mScanCode{0};
+ int32_t mMetaState{AMETA_NONE};
+ int32_t mRepeatCount{0};
+};
+
} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 58ed418..47b9f58 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -60,6 +60,7 @@
"libbinder.cpp",
"parcel.cpp",
"parcel_jni.cpp",
+ "persistable_bundle.cpp",
"process.cpp",
"stability.cpp",
"status.cpp",
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
new file mode 100644
index 0000000..f178027
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/binder_parcel.h>
+#include <android/persistable_bundle.h>
+#include <sys/cdefs.h>
+
+#include <set>
+#include <sstream>
+
+namespace aidl::android::os {
+
+/**
+ * Wrapper class that enables interop with AIDL NDK generation
+ * Takes ownership of the APersistableBundle* given to it in reset() and will automatically
+ * destroy it in the destructor, similar to a smart pointer container
+ */
+class PersistableBundle {
+ public:
+ PersistableBundle() noexcept : mPBundle(APersistableBundle_new()) {}
+ // takes ownership of the APersistableBundle*
+ PersistableBundle(APersistableBundle* _Nonnull bundle) noexcept : mPBundle(bundle) {}
+ // takes ownership of the APersistableBundle*
+ PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {}
+ // duplicates, does not take ownership of the APersistableBundle*
+ PersistableBundle(const PersistableBundle& other) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ mPBundle = APersistableBundle_dup(other.mPBundle);
+ }
+ }
+ // duplicates, does not take ownership of the APersistableBundle*
+ PersistableBundle& operator=(const PersistableBundle& other) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ mPBundle = APersistableBundle_dup(other.mPBundle);
+ }
+ return *this;
+ }
+
+ ~PersistableBundle() { reset(); }
+
+ binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
+ reset();
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_readFromParcel(parcel, &mPBundle);
+ } else {
+ return STATUS_FAILED_TRANSACTION;
+ }
+ }
+
+ binder_status_t writeToParcel(AParcel* _Nonnull parcel) const {
+ if (!mPBundle) {
+ return STATUS_BAD_VALUE;
+ }
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_writeToParcel(mPBundle, parcel);
+ } else {
+ return STATUS_FAILED_TRANSACTION;
+ }
+ }
+
+ /**
+ * Destroys any currently owned APersistableBundle* and takes ownership of the given
+ * APersistableBundle*
+ *
+ * @param pBundle The APersistableBundle to take ownership of
+ */
+ void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept {
+ if (mPBundle) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_delete(mPBundle);
+ }
+ mPBundle = nullptr;
+ }
+ mPBundle = pBundle;
+ }
+
+ /**
+ * Check the actual contents of the bundle for equality. This is typically
+ * what should be used to check for equality.
+ */
+ bool deepEquals(const PersistableBundle& rhs) const {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_isEqual(get(), rhs.get());
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * NOTE: This does NOT check the contents of the PersistableBundle. This is
+ * implemented for ordering. Use deepEquals() to check for equality between
+ * two different PersistableBundle objects.
+ */
+ inline bool operator==(const PersistableBundle& rhs) const { return get() == rhs.get(); }
+ inline bool operator!=(const PersistableBundle& rhs) const { return get() != rhs.get(); }
+
+ inline bool operator<(const PersistableBundle& rhs) const { return get() < rhs.get(); }
+ inline bool operator>(const PersistableBundle& rhs) const { return get() > rhs.get(); }
+ inline bool operator>=(const PersistableBundle& rhs) const { return !(*this < rhs); }
+ inline bool operator<=(const PersistableBundle& rhs) const { return !(*this > rhs); }
+
+ PersistableBundle& operator=(PersistableBundle&& other) noexcept {
+ reset(other.release());
+ return *this;
+ }
+
+ /**
+ * Stops managing any contained APersistableBundle*, returning it to the caller. Ownership
+ * is released.
+ * @return APersistableBundle* or null if this was empty
+ */
+ [[nodiscard]] APersistableBundle* _Nullable release() noexcept {
+ APersistableBundle* _Nullable ret = mPBundle;
+ mPBundle = nullptr;
+ return ret;
+ }
+
+ inline std::string toString() const {
+ if (!mPBundle) {
+ return "<PersistableBundle: null>";
+ } else if (__builtin_available(android __ANDROID_API_V__, *)) {
+ std::ostringstream os;
+ os << "<PersistableBundle: ";
+ os << "size: " << std::to_string(APersistableBundle_size(mPBundle));
+ os << " >";
+ return os.str();
+ }
+ return "<PersistableBundle (unknown)>";
+ }
+
+ int32_t size() const {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_size(mPBundle);
+ } else {
+ return 0;
+ }
+ }
+
+ int32_t erase(const std::string& key) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_erase(mPBundle, key.c_str());
+ } else {
+ return 0;
+ }
+ }
+
+ void putBoolean(const std::string& key, bool val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putBoolean(mPBundle, key.c_str(), val);
+ }
+ }
+
+ void putInt(const std::string& key, int32_t val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putInt(mPBundle, key.c_str(), val);
+ }
+ }
+
+ void putLong(const std::string& key, int64_t val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putLong(mPBundle, key.c_str(), val);
+ }
+ }
+
+ void putDouble(const std::string& key, double val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putDouble(mPBundle, key.c_str(), val);
+ }
+ }
+
+ void putString(const std::string& key, const std::string& val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putString(mPBundle, key.c_str(), val.c_str());
+ }
+ }
+
+ void putBooleanVector(const std::string& key, const std::vector<bool>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ // std::vector<bool> has no ::data().
+ int32_t num = vec.size();
+ if (num > 0) {
+ bool* newVec = (bool*)malloc(num * sizeof(bool));
+ if (newVec) {
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ APersistableBundle_putBooleanVector(mPBundle, key.c_str(), newVec, num);
+ free(newVec);
+ }
+ }
+ }
+ }
+
+ void putIntVector(const std::string& key, const std::vector<int32_t>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t num = vec.size();
+ if (num > 0) {
+ APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num);
+ }
+ }
+ }
+ void putLongVector(const std::string& key, const std::vector<int64_t>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t num = vec.size();
+ if (num > 0) {
+ APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num);
+ }
+ }
+ }
+ void putDoubleVector(const std::string& key, const std::vector<double>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t num = vec.size();
+ if (num > 0) {
+ APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num);
+ }
+ }
+ }
+ void putStringVector(const std::string& key, const std::vector<std::string>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t num = vec.size();
+ if (num > 0) {
+ char** inVec = (char**)malloc(num * sizeof(char*));
+ if (inVec) {
+ for (int32_t i = 0; i < num; i++) {
+ inVec[i] = strdup(vec[i].c_str());
+ }
+ APersistableBundle_putStringVector(mPBundle, key.c_str(), inVec, num);
+ free(inVec);
+ }
+ }
+ }
+ }
+ void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle);
+ }
+ }
+
+ bool getBoolean(const std::string& key, bool* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_getBoolean(mPBundle, key.c_str(), val);
+ } else {
+ return false;
+ }
+ }
+
+ bool getInt(const std::string& key, int32_t* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_getInt(mPBundle, key.c_str(), val);
+ } else {
+ return false;
+ }
+ }
+
+ bool getLong(const std::string& key, int64_t* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_getLong(mPBundle, key.c_str(), val);
+ } else {
+ return false;
+ }
+ }
+
+ bool getDouble(const std::string& key, double* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_getDouble(mPBundle, key.c_str(), val);
+ } else {
+ return false;
+ }
+ }
+
+ static char* _Nullable stringAllocator(int32_t bufferSizeBytes, void* _Nullable) {
+ return (char*)malloc(bufferSizeBytes);
+ }
+
+ bool getString(const std::string& key, std::string* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ char* outString = nullptr;
+ bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString,
+ &stringAllocator, nullptr);
+ if (ret && outString) {
+ *val = std::string(outString);
+ }
+ return ret;
+ } else {
+ return false;
+ }
+ }
+
+ template <typename T>
+ bool getVecInternal(int32_t (*_Nonnull getVec)(const APersistableBundle* _Nonnull,
+ const char* _Nonnull, T* _Nullable, int32_t),
+ const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ std::vector<T>* _Nonnull vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t bytes = 0;
+ // call first with nullptr to get required size in bytes
+ bytes = getVec(pBundle, key, nullptr, 0);
+ if (bytes > 0) {
+ T* newVec = (T*)malloc(bytes);
+ if (newVec) {
+ bytes = getVec(pBundle, key, newVec, bytes);
+ int32_t elements = bytes / sizeof(T);
+ vec->clear();
+ for (int32_t i = 0; i < elements; i++) {
+ vec->push_back(newVec[i]);
+ }
+ free(newVec);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) {
+ return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
+ vec);
+ }
+ bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) {
+ return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
+ vec);
+ }
+ bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) {
+ return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
+ vec);
+ }
+ bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) {
+ return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, key.c_str(),
+ vec);
+ }
+
+ // Takes ownership of and frees the char** and its elements.
+ // Creates a new set or vector based on the array of char*.
+ template <typename T>
+ T moveStringsInternal(char* _Nullable* _Nonnull strings, int32_t bufferSizeBytes) {
+ if (strings && bufferSizeBytes > 0) {
+ int32_t num = bufferSizeBytes / sizeof(char*);
+ T ret;
+ for (int32_t i = 0; i < num; i++) {
+ ret.insert(ret.end(), std::string(strings[i]));
+ free(strings[i]);
+ }
+ free(strings);
+ return ret;
+ }
+ return T();
+ }
+
+ bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) {
+ int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
+ &stringAllocator, nullptr);
+ if (bytes > 0) {
+ char** strings = (char**)malloc(bytes);
+ if (strings) {
+ bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, bytes,
+ &stringAllocator, nullptr);
+ *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle* bundle = nullptr;
+ bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle);
+ if (ret) {
+ *val = PersistableBundle(bundle);
+ }
+ return ret;
+ } else {
+ return false;
+ }
+ }
+
+ std::set<std::string> getKeys(
+ int32_t (*_Nonnull getTypedKeys)(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable),
+ const APersistableBundle* _Nonnull pBundle) {
+ // call first with nullptr to get required size in bytes
+ int32_t bytes = getTypedKeys(pBundle, nullptr, 0, &stringAllocator, nullptr);
+ if (bytes > 0) {
+ char** keys = (char**)malloc(bytes);
+ if (keys) {
+ bytes = getTypedKeys(pBundle, keys, bytes, &stringAllocator, nullptr);
+ return moveStringsInternal<std::set<std::string>>(keys, bytes);
+ }
+ }
+ return {};
+ }
+
+ std::set<std::string> getBooleanKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getBooleanKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getIntKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getIntKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getLongKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getLongKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getDoubleKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getDoubleKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getStringKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getStringKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getBooleanVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getIntVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getLongVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getDoubleVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getStringVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getPersistableBundleKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getMonKeys() {
+ // :P
+ return {"c(o,o)b", "c(o,o)b"};
+ }
+
+ private:
+ inline APersistableBundle* _Nullable get() const { return mPBundle; }
+ APersistableBundle* _Nullable mPBundle = nullptr;
+};
+
+} // namespace aidl::android::os
diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
new file mode 100644
index 0000000..eff8104
--- /dev/null
+++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
@@ -0,0 +1,905 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/binder_parcel.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/*
+ * A mapping from string keys to values of various types.
+ * See frameworks/base/core/java/android/os/PersistableBundle.java
+ * for the Java type than can be used in SDK APIs.
+ * APersistableBundle exists to be used in AIDL interfaces and seamlessly
+ * interact with framework services.
+ * frameworks/native/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+ * contains the AIDL type used in the ndk backend of AIDL interfaces.
+ */
+struct APersistableBundle;
+typedef struct APersistableBundle APersistableBundle;
+
+/**
+ * This is a user supplied allocator that allocates a buffer for the
+ * APersistableBundle APIs to fill in with a string.
+ *
+ * \param the required size in bytes for the allocated buffer
+ * \param void* _Nullable context if needed by the callback
+ *
+ * \return allocated buffer of sizeBytes. Null if allocation failed.
+ */
+typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t sizeBytes,
+ void* _Nullable context);
+
+/**
+ * Create a new APersistableBundle.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \return Pointer to a new APersistableBundle
+ */
+APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Create a new APersistableBundle based off an existing APersistableBundle.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to duplicate
+ *
+ * \return Pointer to a new APersistableBundle
+ */
+APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _Nonnull pBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Delete an APersistableBundle. This must always be called when finished using
+ * the object.
+ *
+ * \param bundle to delete
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_delete(APersistableBundle* _Nonnull pBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Check for equality of APersistableBundles.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param lhs bundle to compare agains the other param
+ * \param rhs bundle to compare agains the other param
+ *
+ * \return true when equal, false when not
+ */
+bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs,
+ const APersistableBundle* _Nonnull rhs)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Read an APersistableBundle from an AParcel.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param parcel to read from
+ * \param outPBundle bundle to write to
+ *
+ * \return STATUS_OK on success
+ * STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an
+ * issue deserializing (eg, corrupted parcel)
+ * STATUS_BAD_TYPE if the parcel's current data position is not that of
+ * an APersistableBundle type
+ * STATUS_NO_MEMORY if an allocation fails
+ */
+binder_status_t APersistableBundle_readFromParcel(
+ const AParcel* _Nonnull parcel, APersistableBundle* _Nullable* _Nonnull outPBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Write an APersistableBundle to an AParcel.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param pBundle bundle to write to the parcel
+ * \param parcel to write to
+ *
+ * \return STATUS_OK on success.
+ * STATUS_BAD_VALUE if either pBundle or parcel is null, or if the
+ * APersistableBundle*
+ * fails to serialize (eg, internally corrupted)
+ * STATUS_NO_MEMORY if the parcel runs out of space to store the pBundle & is
+ * unable to allocate more
+ * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs
+ */
+binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonnull pBundle,
+ AParcel* _Nonnull parcel)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get the size of an APersistableBundle. This is the number of mappings in the
+ * object.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to get the size of (number of mappings)
+ *
+ * \return number of mappings in the object
+ */
+int32_t APersistableBundle_size(APersistableBundle* _Nonnull pBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Erase any entries added with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping to erase
+ *
+ * \return number of entries erased. Either 0 or 1.
+ */
+int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a boolean associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ bool val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an int32_t associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ int32_t val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an int64_t associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ int64_t val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a double associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ double val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a string associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a boolean vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, const bool* _Nonnull vec,
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an int32_t vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ const int32_t* _Nonnull vec, int32_t num)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an int64_t vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, const int64_t* _Nonnull vec,
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a double vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, const double* _Nonnull vec,
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a string vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key,
+ const char* _Nullable const* _Nullable vec, int32_t num)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an APersistableBundle associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key,
+ const APersistableBundle* _Nonnull val)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a boolean associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, bool* _Nonnull val)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an int32_t associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an int64_t associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, int64_t* _Nonnull val)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a double associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, double* _Nonnull val)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a string associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of string associated with the provided key on success
+ * 0 if no string exists for the provided key
+ * -1 if the provided allocator fails and returns false
+ */
+int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, char* _Nullable* _Nonnull val,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a boolean vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ */
+int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, bool* _Nullable buffer,
+ int32_t bufferSizeBytes)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an int32_t vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ */
+int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, int32_t* _Nullable buffer,
+ int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an int64_t vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ */
+int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, int64_t* _Nullable buffer,
+ int32_t bufferSizeBytes)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a double vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ */
+int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, double* _Nullable buffer,
+ int32_t bufferSizeBytes)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a string vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key,
+ char* _Nullable* _Nullable buffer,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an APersistableBundle* associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to an APersistableBundle pointer to write to point to
+ * a new copy of the stored APersistableBundle. The caller takes ownership of
+ * the new APersistableBundle and must be deleted with
+ * APersistableBundle_delete.
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getPersistableBundle(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key,
+ APersistableBundle* _Nullable* _Nonnull outBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getPersistableBundleKeys(
+ const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
+
+__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 1c5f79f..0843a8e 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -161,6 +161,51 @@
AServiceManager_addServiceWithFlags; # systemapi llndk
};
+LIBBINDER_NDK35 { # introduced=VanillaIceCream
+ global:
+ APersistableBundle_readFromParcel;
+ APersistableBundle_writeToParcel;
+ APersistableBundle_new;
+ APersistableBundle_dup;
+ APersistableBundle_delete;
+ APersistableBundle_isEqual;
+ APersistableBundle_size;
+ APersistableBundle_erase;
+ APersistableBundle_putBoolean;
+ APersistableBundle_putInt;
+ APersistableBundle_putLong;
+ APersistableBundle_putDouble;
+ APersistableBundle_putString;
+ APersistableBundle_putBooleanVector;
+ APersistableBundle_putIntVector;
+ APersistableBundle_putLongVector;
+ APersistableBundle_putDoubleVector;
+ APersistableBundle_putStringVector;
+ APersistableBundle_putPersistableBundle;
+ APersistableBundle_getBoolean;
+ APersistableBundle_getInt;
+ APersistableBundle_getLong;
+ APersistableBundle_getDouble;
+ APersistableBundle_getString;
+ APersistableBundle_getBooleanVector;
+ APersistableBundle_getIntVector;
+ APersistableBundle_getLongVector;
+ APersistableBundle_getDoubleVector;
+ APersistableBundle_getStringVector;
+ APersistableBundle_getPersistableBundle;
+ APersistableBundle_getBooleanKeys;
+ APersistableBundle_getIntKeys;
+ APersistableBundle_getLongKeys;
+ APersistableBundle_getDoubleKeys;
+ APersistableBundle_getStringKeys;
+ APersistableBundle_getBooleanVectorKeys;
+ APersistableBundle_getIntVectorKeys;
+ APersistableBundle_getLongVectorKeys;
+ APersistableBundle_getDoubleVectorKeys;
+ APersistableBundle_getStringVectorKeys;
+ APersistableBundle_getPersistableBundleKeys;
+};
+
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
diff --git a/libs/binder/ndk/persistable_bundle.cpp b/libs/binder/ndk/persistable_bundle.cpp
new file mode 100644
index 0000000..404611c
--- /dev/null
+++ b/libs/binder/ndk/persistable_bundle.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/binder_libbinder.h>
+#include <android/persistable_bundle.h>
+#include <binder/PersistableBundle.h>
+#include <log/log.h>
+#include <persistable_bundle_internal.h>
+#include <string.h>
+
+#include <set>
+
+__BEGIN_DECLS
+
+struct APersistableBundle {
+ APersistableBundle(const APersistableBundle& pBundle) : mPBundle(pBundle.mPBundle) {}
+ APersistableBundle(const android::os::PersistableBundle& pBundle) : mPBundle(pBundle) {}
+ APersistableBundle() = default;
+ android::os::PersistableBundle mPBundle;
+};
+
+APersistableBundle* _Nullable APersistableBundle_new() {
+ return new (std::nothrow) APersistableBundle();
+}
+
+APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* pBundle) {
+ if (pBundle) {
+ return new APersistableBundle(*pBundle);
+ } else {
+ return new APersistableBundle();
+ }
+}
+
+void APersistableBundle_delete(APersistableBundle* pBundle) {
+ free(pBundle);
+}
+
+bool APersistableBundle_isEqual(const APersistableBundle* lhs, const APersistableBundle* rhs) {
+ if (lhs && rhs) {
+ return lhs->mPBundle == rhs->mPBundle;
+ } else if (lhs == rhs) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+binder_status_t APersistableBundle_readFromParcel(const AParcel* parcel,
+ APersistableBundle* _Nullable* outPBundle) {
+ if (!parcel || !outPBundle) return STATUS_BAD_VALUE;
+ APersistableBundle* newPBundle = APersistableBundle_new();
+ if (newPBundle == nullptr) return STATUS_NO_MEMORY;
+ binder_status_t status =
+ newPBundle->mPBundle.readFromParcel(AParcel_viewPlatformParcel(parcel));
+ if (status == STATUS_OK) {
+ *outPBundle = newPBundle;
+ }
+ return status;
+}
+
+binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* pBundle,
+ AParcel* parcel) {
+ if (!parcel || !pBundle) return STATUS_BAD_VALUE;
+ return pBundle->mPBundle.writeToParcel(AParcel_viewPlatformParcel(parcel));
+}
+
+int32_t APersistableBundle_size(APersistableBundle* pBundle) {
+ size_t size = pBundle->mPBundle.size();
+ LOG_ALWAYS_FATAL_IF(size > INT32_MAX,
+ "The APersistableBundle has gotten too large! There will be an overflow in "
+ "the reported size.");
+ return pBundle->mPBundle.size();
+}
+int32_t APersistableBundle_erase(APersistableBundle* pBundle, const char* key) {
+ return pBundle->mPBundle.erase(android::String16(key));
+}
+void APersistableBundle_putBoolean(APersistableBundle* pBundle, const char* key, bool val) {
+ pBundle->mPBundle.putBoolean(android::String16(key), val);
+}
+void APersistableBundle_putInt(APersistableBundle* pBundle, const char* key, int32_t val) {
+ pBundle->mPBundle.putInt(android::String16(key), val);
+}
+void APersistableBundle_putLong(APersistableBundle* pBundle, const char* key, int64_t val) {
+ pBundle->mPBundle.putLong(android::String16(key), val);
+}
+void APersistableBundle_putDouble(APersistableBundle* pBundle, const char* key, double val) {
+ pBundle->mPBundle.putDouble(android::String16(key), val);
+}
+void APersistableBundle_putString(APersistableBundle* pBundle, const char* key, const char* val) {
+ pBundle->mPBundle.putString(android::String16(key), android::String16(val));
+}
+void APersistableBundle_putBooleanVector(APersistableBundle* pBundle, const char* key,
+ const bool* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<bool> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ pBundle->mPBundle.putBooleanVector(android::String16(key), newVec);
+}
+void APersistableBundle_putIntVector(APersistableBundle* pBundle, const char* key,
+ const int32_t* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<int32_t> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ pBundle->mPBundle.putIntVector(android::String16(key), newVec);
+}
+void APersistableBundle_putLongVector(APersistableBundle* pBundle, const char* key,
+ const int64_t* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<int64_t> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ pBundle->mPBundle.putLongVector(android::String16(key), newVec);
+}
+void APersistableBundle_putDoubleVector(APersistableBundle* pBundle, const char* key,
+ const double* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<double> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ pBundle->mPBundle.putDoubleVector(android::String16(key), newVec);
+}
+void APersistableBundle_putStringVector(APersistableBundle* pBundle, const char* key,
+ const char* const* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<android::String16> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = android::String16(vec[i]);
+ }
+ pBundle->mPBundle.putStringVector(android::String16(key), newVec);
+}
+void APersistableBundle_putPersistableBundle(APersistableBundle* pBundle, const char* key,
+ const APersistableBundle* val) {
+ pBundle->mPBundle.putPersistableBundle(android::String16(key), val->mPBundle);
+}
+bool APersistableBundle_getBoolean(const APersistableBundle* pBundle, const char* key, bool* val) {
+ return pBundle->mPBundle.getBoolean(android::String16(key), val);
+}
+bool APersistableBundle_getInt(const APersistableBundle* pBundle, const char* key, int32_t* val) {
+ return pBundle->mPBundle.getInt(android::String16(key), val);
+}
+bool APersistableBundle_getLong(const APersistableBundle* pBundle, const char* key, int64_t* val) {
+ return pBundle->mPBundle.getLong(android::String16(key), val);
+}
+bool APersistableBundle_getDouble(const APersistableBundle* pBundle, const char* key, double* val) {
+ return pBundle->mPBundle.getDouble(android::String16(key), val);
+}
+int32_t APersistableBundle_getString(const APersistableBundle* pBundle, const char* key, char** val,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ android::String16 outVal;
+ bool ret = pBundle->mPBundle.getString(android::String16(key), &outVal);
+ if (ret) {
+ android::String8 tmp8(outVal);
+ *val = stringAllocator(tmp8.bytes() + 1, context);
+ if (*val) {
+ strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1);
+ return tmp8.bytes();
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+int32_t APersistableBundle_getBooleanVector(const APersistableBundle* pBundle, const char* key,
+ bool* buffer, int32_t bufferSizeBytes) {
+ std::vector<bool> newVec;
+ pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec);
+ return getVecInternal<bool>(newVec, buffer, bufferSizeBytes);
+}
+int32_t APersistableBundle_getIntVector(const APersistableBundle* pBundle, const char* key,
+ int32_t* buffer, int32_t bufferSizeBytes) {
+ std::vector<int32_t> newVec;
+ pBundle->mPBundle.getIntVector(android::String16(key), &newVec);
+ return getVecInternal<int32_t>(newVec, buffer, bufferSizeBytes);
+}
+int32_t APersistableBundle_getLongVector(const APersistableBundle* pBundle, const char* key,
+ int64_t* buffer, int32_t bufferSizeBytes) {
+ std::vector<int64_t> newVec;
+ pBundle->mPBundle.getLongVector(android::String16(key), &newVec);
+ return getVecInternal<int64_t>(newVec, buffer, bufferSizeBytes);
+}
+int32_t APersistableBundle_getDoubleVector(const APersistableBundle* pBundle, const char* key,
+ double* buffer, int32_t bufferSizeBytes) {
+ std::vector<double> newVec;
+ pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec);
+ return getVecInternal<double>(newVec, buffer, bufferSizeBytes);
+}
+int32_t APersistableBundle_getStringVector(const APersistableBundle* pBundle, const char* key,
+ char** vec, int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::vector<android::String16> newVec;
+ pBundle->mPBundle.getStringVector(android::String16(key), &newVec);
+ return getStringsInternal<std::vector<android::String16>>(newVec, vec, bufferSizeBytes,
+ stringAllocator, context);
+}
+bool APersistableBundle_getPersistableBundle(const APersistableBundle* pBundle, const char* key,
+ APersistableBundle** outBundle) {
+ APersistableBundle* bundle = APersistableBundle_new();
+ bool ret = pBundle->mPBundle.getPersistableBundle(android::String16(key), &bundle->mPBundle);
+ if (ret) {
+ *outBundle = bundle;
+ return true;
+ }
+ return false;
+}
+int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getBooleanKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getIntKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getIntKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getLongKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getLongKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getDoubleKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getStringKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getStringKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getBooleanVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getIntVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getLongVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getDoubleVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getStringVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getPersistableBundleKeys(
+ const APersistableBundle* pBundle, char** outKeys, int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator, void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getPersistableBundleKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+
+__END_DECLS
diff --git a/libs/binder/ndk/persistable_bundle_internal.h b/libs/binder/ndk/persistable_bundle_internal.h
new file mode 100644
index 0000000..279c66f
--- /dev/null
+++ b/libs/binder/ndk/persistable_bundle_internal.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/persistable_bundle.h>
+#include <log/log.h>
+#include <utils/String8.h>
+
+// take a vector and put the contents into a buffer.
+// return the size of the contents.
+// This may not put all of the contents into the buffer if the buffer is not
+// large enough.
+template <typename T>
+int32_t getVecInternal(const std::vector<T>& inVec, T* _Nullable buffer, int32_t bufferSizeBytes) {
+ LOG_ALWAYS_FATAL_IF(inVec.size() > INT32_MAX,
+ "The size of the APersistableBundle has gotten too large!");
+ LOG_ALWAYS_FATAL_IF(
+ bufferSizeBytes < 0,
+ "The buffer size in bytes can not be larger than INT32_MAX and can not be negative.");
+ int32_t num = inVec.size();
+ int32_t numAvailable = bufferSizeBytes / sizeof(T);
+ int32_t numFill = numAvailable < num ? numAvailable : num;
+
+ if (numFill > 0 && buffer) {
+ for (int32_t i = 0; i < numFill; i++) {
+ buffer[i] = inVec[i];
+ }
+ }
+ return num * sizeof(T);
+}
+
+// take a vector or a set of String16 and put the contents into a char** buffer.
+// return the size of the contents.
+// This may not put all of the contents into the buffer if the buffer is not
+// large enough.
+// The strings are duped with a user supplied callback
+template <typename T>
+int32_t getStringsInternal(const T& strings, char* _Nullable* _Nullable buffer,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) {
+ LOG_ALWAYS_FATAL_IF(strings.size() > INT32_MAX,
+ "The size of the APersistableBundle has gotten too large!");
+ LOG_ALWAYS_FATAL_IF(
+ bufferSizeBytes < 0,
+ "The buffer size in bytes can not be larger than INT32_MAX and can not be negative.");
+ int32_t num = strings.size();
+ int32_t numAvailable = bufferSizeBytes / sizeof(char*);
+ int32_t numFill = numAvailable < num ? numAvailable : num;
+ if (!stringAllocator) {
+ return -1;
+ }
+
+ if (numFill > 0 && buffer) {
+ int32_t i = 0;
+ for (const auto& val : strings) {
+ android::String8 tmp8 = android::String8(val);
+ buffer[i] = stringAllocator(tmp8.bytes() + 1, context);
+ if (buffer[i] == nullptr) {
+ return -1;
+ }
+ strncpy(buffer[i], tmp8.c_str(), tmp8.bytes() + 1);
+ i++;
+ if (i > numFill - 1) {
+ // buffer is too small to keep going or this is the end of the
+ // set
+ break;
+ }
+ }
+ }
+ return num * sizeof(char*);
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index a08cb7a..78f8877 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -21,6 +21,7 @@
use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
use crate::sys;
+use downcast_rs::{impl_downcast, DowncastSync};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::convert::TryFrom;
@@ -51,7 +52,7 @@
/// interfaces) must implement this trait.
///
/// This is equivalent `IInterface` in C++.
-pub trait Interface: Send + Sync {
+pub trait Interface: Send + Sync + DowncastSync {
/// Convert this binder object into a generic [`SpIBinder`] reference.
fn as_binder(&self) -> SpIBinder {
panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
@@ -66,6 +67,8 @@
}
}
+impl_downcast!(sync Interface);
+
/// Implemented by sync interfaces to specify what the associated async interface is.
/// Generic to handle the fact that async interfaces are generic over a thread pool.
///
@@ -143,7 +146,7 @@
/// When using the AIDL backend, users need only implement the high-level AIDL-defined
/// interface. The AIDL compiler then generates a container struct that wraps
/// the user-defined service and implements `Remotable`.
-pub trait Remotable: Send + Sync {
+pub trait Remotable: Send + Sync + 'static {
/// The Binder interface descriptor string.
///
/// This string is a unique identifier for a Binder interface, and should be
@@ -893,6 +896,23 @@
$crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
+
+ /// Tries to downcast the interface to another type.
+ /// When receiving this object from a binder call, make sure that the object received is
+ /// a binder native object and that is of the right type for the Downcast:
+ ///
+ /// let binder = received_object.as_binder();
+ /// if !binder.is_remote() {
+ /// let binder_native: Binder<BnFoo> = binder.try_into()?;
+ /// let original_object = binder_native.downcast_binder::<MyFoo>();
+ /// // Check that returned type is not None before using it
+ /// }
+ ///
+ /// Handle the error cases instead of just calling `unwrap` or `expect` to prevent a
+ /// malicious caller to mount a Denial of Service attack.
+ pub fn downcast_binder<T: $interface>(&self) -> Option<&T> {
+ self.0.as_any().downcast_ref::<T>()
+ }
}
impl $crate::binder_impl::Remotable for $native {
@@ -1004,7 +1024,7 @@
$(
// Async interface trait implementations.
- impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::FromIBinder for dyn $async_interface<P> {
fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> {
use $crate::binder_impl::AssociateClass;
@@ -1030,27 +1050,27 @@
}
}
- impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
let binder = $crate::Interface::as_binder(self);
parcel.write(&binder)
}
}
- impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&this.map($crate::Interface::as_binder))
}
}
- impl<P: $crate::BinderAsyncPool> std::fmt::Debug for dyn $async_interface<P> + '_ {
+ impl<P: $crate::BinderAsyncPool + 'static> std::fmt::Debug for dyn $async_interface<P> + '_ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(stringify!($async_interface))
}
}
/// Convert a &dyn $async_interface to Strong<dyn $async_interface>
- impl<P: $crate::BinderAsyncPool> std::borrow::ToOwned for dyn $async_interface<P> {
+ impl<P: $crate::BinderAsyncPool + 'static> std::borrow::ToOwned for dyn $async_interface<P> {
type Owned = $crate::Strong<dyn $async_interface<P>>;
fn to_owned(&self) -> Self::Owned {
self.as_binder().into_interface()
@@ -1058,11 +1078,11 @@
}
}
- impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
type Target = dyn $async_interface<P>;
}
- impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
type Target = dyn $interface;
}
)?
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 5c688fa..6afe5ab 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -22,29 +22,28 @@
use crate::error::{status_result, Result, StatusCode};
use crate::sys;
-use std::fs::File;
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
/// Rust version of the Java class android.os.ParcelFileDescriptor
#[derive(Debug)]
-pub struct ParcelFileDescriptor(File);
+pub struct ParcelFileDescriptor(OwnedFd);
impl ParcelFileDescriptor {
/// Create a new `ParcelFileDescriptor`
- pub fn new(file: File) -> Self {
- Self(file)
+ pub fn new<F: Into<OwnedFd>>(fd: F) -> Self {
+ Self(fd.into())
}
}
-impl AsRef<File> for ParcelFileDescriptor {
- fn as_ref(&self) -> &File {
+impl AsRef<OwnedFd> for ParcelFileDescriptor {
+ fn as_ref(&self) -> &OwnedFd {
&self.0
}
}
-impl From<ParcelFileDescriptor> for File {
- fn from(file: ParcelFileDescriptor) -> File {
- file.0
+impl From<ParcelFileDescriptor> for OwnedFd {
+ fn from(fd: ParcelFileDescriptor) -> OwnedFd {
+ fd.0
}
}
@@ -120,7 +119,7 @@
// Safety: At this point, we know that the file descriptor was
// not -1, so must be a valid, owned file descriptor which we
// can safely turn into a `File`.
- let file = unsafe { File::from_raw_fd(fd) };
+ let file = unsafe { OwnedFd::from_raw_fd(fd) };
Ok(Some(ParcelFileDescriptor::new(file)))
}
}
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index 37f182e..fbedfee 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -58,7 +58,7 @@
let wrong_service: Result<binder::Strong<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
binder::get_interface(service_name);
match wrong_service {
- Err(e) if e == StatusCode::BAD_TYPE => {}
+ Err(StatusCode::BAD_TYPE) => {}
Err(e) => {
eprintln!("Trying to use a service via the wrong interface errored with unexpected error {:?}", e);
return e as c_int;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index bc34d4c..bbb310a 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -35,6 +35,7 @@
#include <trusty/tipc.h>
#endif // BINDER_RPC_TO_TRUSTY_TEST
+#include "../Utils.h"
#include "binderRpcTestCommon.h"
#include "binderRpcTestFixture.h"
@@ -1197,7 +1198,7 @@
{.fd = serverFd.get(), .events = POLLIN, .revents = 0},
{.fd = connectFd.get(), .events = POLLOUT, .revents = 0},
};
- ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+ ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1));
LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno));
if (pfd[0].revents & POLLIN) {
diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json
index 1cefac5..6e20b8a 100644
--- a/libs/binder/trusty/binderRpcTest/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/manifest.json
@@ -2,5 +2,5 @@
"uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b",
"app_name": "binderRpcTest",
"min_heap": 262144,
- "min_stack": 16384
+ "min_stack": 20480
}
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 5b34ba1..b6a47fb 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -318,35 +318,44 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot);
- std::lock_guard<std::mutex> lock(mCore->mMutex);
+ sp<IProducerListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
- return NO_INIT;
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mSharedBufferMode || slot == mCore->mSharedBufferSlot) {
+ BQ_LOGE("detachBuffer: detachBuffer not allowed in shared buffer mode");
+ return BAD_VALUE;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
+ slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mBufferState.isAcquired()) {
+ BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
+ return BAD_VALUE;
+ }
+ if (mCore->mBufferReleasedCbEnabled) {
+ listener = mCore->mConnectedProducerListener;
+ }
+
+ mSlots[slot].mBufferState.detachConsumer();
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeSlots.insert(slot);
+ mCore->clearBufferSlotLocked(slot);
+ mCore->mDequeueCondition.notify_all();
+ VALIDATE_CONSISTENCY();
}
- if (mCore->mSharedBufferMode || slot == mCore->mSharedBufferSlot) {
- BQ_LOGE("detachBuffer: detachBuffer not allowed in shared buffer mode");
- return BAD_VALUE;
+ if (listener) {
+ listener->onBufferDetached(slot);
}
-
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
- } else if (!mSlots[slot].mBufferState.isAcquired()) {
- BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- return BAD_VALUE;
- }
-
- mSlots[slot].mBufferState.detachConsumer();
- mCore->mActiveBuffers.erase(slot);
- mCore->mFreeSlots.insert(slot);
- mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.notify_all();
- VALIDATE_CONSISTENCY();
-
return NO_ERROR;
}
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index f7ffbb9..b15f501 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -49,6 +49,12 @@
// onBuffersFreed is called from IGraphicBufferConsumer::discardFreeBuffers
// to notify the producer that certain free buffers are discarded by the consumer.
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous
+ // onBufferDetached is called from IGraphicBufferConsumer::detachBuffer to
+ // notify the producer that a buffer slot is free and ready to be dequeued.
+ //
+ // This is called without any lock held and can be called concurrently by
+ // multiple threads.
+ virtual void onBufferDetached(int /*slot*/) {} // Asynchronous
};
#ifndef NO_BINDER
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 1410c7d..df7739c 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -1187,6 +1187,76 @@
ASSERT_EQ(true, output.bufferReplaced);
}
+struct BufferDetachedListener : public BnProducerListener {
+public:
+ BufferDetachedListener() = default;
+ virtual ~BufferDetachedListener() = default;
+
+ virtual void onBufferReleased() {}
+ virtual bool needsReleaseNotify() { return true; }
+ virtual void onBufferDetached(int slot) {
+ mDetachedSlots.push_back(slot);
+ }
+ const std::vector<int>& getDetachedSlots() const { return mDetachedSlots; }
+private:
+ std::vector<int> mDetachedSlots;
+};
+
+TEST_F(BufferQueueTest, TestConsumerDetachProducerListener) {
+ createBufferQueue();
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<BufferDetachedListener> pl(new BufferDetachedListener);
+ ASSERT_EQ(OK, mProducer->connect(pl, NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK, mProducer->setDequeueTimeout(0));
+ ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
+
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+
+ int slots[2] = {};
+ status_t result = OK;
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
+
+ result = mProducer->dequeueBuffer(&slots[0], &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[0], &buffer));
+
+ result = mProducer->dequeueBuffer(&slots[1], &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[1], &buffer));
+
+ // Queue & detach one from two dequeued buffes.
+ ASSERT_EQ(OK, mProducer->queueBuffer(slots[1], input, &output));
+ BufferItem item{};
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->detachBuffer(item.mSlot));
+
+ // Check whether the slot from IProducerListener is same to the detached slot.
+ ASSERT_EQ(pl->getDetachedSlots().size(), 1);
+ ASSERT_EQ(pl->getDetachedSlots()[0], slots[1]);
+
+ // Dequeue another buffer.
+ int slot;
+ result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+
+ // Dequeue should fail here, since we dequeued 3 buffers and one buffer was
+ // detached from consumer(Two buffers are dequeued, and the current max
+ // dequeued buffer count is two).
+ result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_TRUE(result == WOULD_BLOCK || result == TIMED_OUT || result == INVALID_OPERATION);
+}
+
TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
createBufferQueue();
sp<MockConsumer> mc(new MockConsumer);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 86ced2c..0e26544 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -208,21 +208,6 @@
ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
}
- static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults) {
- const auto sf = ComposerServiceAIDL::getComposerService();
- SurfaceComposerClient::Transaction().apply(true);
-
- const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- binder::Status status = sf->captureDisplay(captureArgs, captureListener);
- status_t err = gui::aidl_utils::statusTFromBinderStatus(status);
- if (err != NO_ERROR) {
- return err;
- }
- captureResults = captureListener->waitForResults();
- return fenceStatus(captureResults.fenceResult);
- }
-
sp<Surface> mSurface;
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mSurfaceControl;
@@ -260,56 +245,6 @@
EXPECT_EQ(1, result);
}
-// This test probably doesn't belong here.
-TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) {
- sp<ANativeWindow> anw(mSurface);
-
- // Verify the screenshot works with no protected buffers.
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- // display 0 is picked for now, can extend to support all displays if needed
- const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- ASSERT_FALSE(display == nullptr);
-
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = display;
- captureArgs.width = 64;
- captureArgs.height = 64;
-
- ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
-
- ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
- NATIVE_WINDOW_API_CPU));
- // Set the PROTECTED usage bit and verify that the screenshot fails. Note
- // that we need to dequeue a buffer in order for it to actually get
- // allocated in SurfaceFlinger.
- ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(),
- GRALLOC_USAGE_PROTECTED));
- ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3));
- ANativeWindowBuffer* buf = nullptr;
-
- status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf);
- if (err) {
- // we could fail if GRALLOC_USAGE_PROTECTED is not supported.
- // that's okay as long as this is the reason for the failure.
- // try again without the GRALLOC_USAGE_PROTECTED bit.
- ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0));
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
- &buf));
- return;
- }
- ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf, -1));
-
- for (int i = 0; i < 4; i++) {
- // Loop to make sure SurfaceFlinger has retired a protected buffer.
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
- &buf));
- ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
- }
- ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
-}
-
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
sp<ANativeWindow> anw(mSurface);
int result = -123;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 69a4f0a..c37db16 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -37,14 +37,14 @@
// flags
/////////////////////////////////////////////////
aconfig_declarations {
- name: "aconfig_input_flags",
+ name: "com.android.input.flags-aconfig",
package: "com.android.input.flags",
srcs: ["input_flags.aconfig"],
}
cc_aconfig_library {
- name: "aconfig_input_flags_c_lib",
- aconfig_declarations: "aconfig_input_flags",
+ name: "com.android.input.flags-aconfig-cc",
+ aconfig_declarations: "com.android.input.flags-aconfig",
host_supported: true,
// Use the test version of the aconfig flag library by default to allow tests to set local
// overrides for flags, without having to link against a separate version of libinput or of this
@@ -242,7 +242,7 @@
],
whole_static_libs: [
- "aconfig_input_flags_c_lib",
+ "com.android.input.flags-aconfig-cc",
"libinput_rust_ffi",
],
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
index 86dcaef..0eeca54 100644
--- a/libs/permission/Android.bp
+++ b/libs/permission/Android.bp
@@ -20,12 +20,6 @@
],
}
-filegroup {
- name: "framework-permission-aidl-filegroup",
- srcs: ["aidl/android/**/*.aidl"],
- path: "aidl",
-}
-
cc_library {
name: "libpermission",
host_supported: true,
@@ -41,7 +35,6 @@
"-Werror",
],
srcs: [
- ":framework-permission-aidl-filegroup",
"AppOpsManager.cpp",
"IAppOpsCallback.cpp",
"IAppOpsService.cpp",
diff --git a/libs/permission/AppOpsManager.cpp b/libs/permission/AppOpsManager.cpp
index b407d02..6959274 100644
--- a/libs/permission/AppOpsManager.cpp
+++ b/libs/permission/AppOpsManager.cpp
@@ -31,9 +31,6 @@
namespace android {
-using ::android::String16;
-using ::android::String8;
-
static const sp<IBinder>& getClientId() {
static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
static sp<IBinder> gClientId;
@@ -46,11 +43,6 @@
return gClientId;
}
-
-static std::string getString(const String16& stringToConvert) {
- return std::string(String8(stringToConvert).c_str());
-}
-
AppOpsManager::AppOpsManager()
{
}
@@ -86,14 +78,9 @@
int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
{
sp<IAppOpsService> service = getService();
- if (service == nullptr) {
- return AppOpsManager::MODE_IGNORED;
- }
- AttributionSourceState attributionSourceState;
- attributionSourceState.uid = uid;
- attributionSourceState.packageName = getString(callingPackage);
-
- return service->checkOperationWithState(op, attributionSourceState);
+ return service != nullptr
+ ? service->checkOperation(op, uid, callingPackage)
+ : AppOpsManager::MODE_IGNORED;
}
int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
@@ -112,18 +99,12 @@
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
const std::optional<String16>& attributionTag, const String16& message) {
sp<IAppOpsService> service = getService();
- if (service == nullptr) {
- return AppOpsManager::MODE_IGNORED;
- }
- AttributionSourceState attributionSourceState;
- attributionSourceState.uid = uid;
- attributionSourceState.packageName = getString(callingPackage);
- if (attributionTag.has_value()) {
- attributionSourceState.attributionTag = getString(attributionTag.value());
- }
+ int32_t mode = service != nullptr
+ ? service->noteOperation(op, uid, callingPackage, attributionTag,
+ shouldCollectNotes(op), message, uid == AID_SYSTEM)
+ : AppOpsManager::MODE_IGNORED;
- return service->noteOperationWithState(op, attributionSourceState,
- shouldCollectNotes(op), message, uid == AID_SYSTEM);
+ return mode;
}
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
@@ -136,18 +117,13 @@
bool startIfModeDefault, const std::optional<String16>& attributionTag,
const String16& message) {
sp<IAppOpsService> service = getService();
- if (service == nullptr) {
- return AppOpsManager::MODE_IGNORED;
- }
- AttributionSourceState attributionSourceState;
- attributionSourceState.uid = uid;
- attributionSourceState.packageName = getString(callingPackage);
- if (attributionTag.has_value()) {
- attributionSourceState.attributionTag = getString(attributionTag.value());
- }
+ int32_t mode = service != nullptr
+ ? service->startOperation(getClientId(), op, uid, callingPackage,
+ attributionTag, startIfModeDefault, shouldCollectNotes(op), message,
+ uid == AID_SYSTEM)
+ : AppOpsManager::MODE_IGNORED;
- return service->startOperationWithState(getClientId(), op, attributionSourceState,
- startIfModeDefault,shouldCollectNotes(op), message, uid == AID_SYSTEM);
+ return mode;
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
@@ -157,16 +133,9 @@
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
const std::optional<String16>& attributionTag) {
sp<IAppOpsService> service = getService();
- if (service == nullptr) {
- return;
+ if (service != nullptr) {
+ service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
}
- AttributionSourceState attributionSourceState;
- attributionSourceState.uid = uid;
- attributionSourceState.packageName = getString(callingPackage);
- if (attributionTag.has_value()) {
- attributionSourceState.attributionTag = getString(attributionTag.value());
- }
- service->finishOperationWithState(getClientId(), op, attributionSourceState);
}
void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName,
diff --git a/libs/permission/IAppOpsService.cpp b/libs/permission/IAppOpsService.cpp
index 33dd24d..7f235a4 100644
--- a/libs/permission/IAppOpsService.cpp
+++ b/libs/permission/IAppOpsService.cpp
@@ -26,8 +26,6 @@
namespace android {
-using android::content::AttributionSourceState;
-
// ----------------------------------------------------------------------
class BpAppOpsService : public BpInterface<IAppOpsService>
@@ -38,30 +36,31 @@
{
}
- virtual int32_t checkOperationWithState(int32_t code,
- const AttributionSourceState &attributionSourceState) {
+ virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
- data.writeParcelable(attributionSourceState);
- remote()->transact(CHECK_OPERATION_WITH_STATE_TRANSACTION, data, &reply);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(CHECK_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
return reply.readInt32();
}
- virtual int32_t noteOperationWithState(int32_t code,
- const AttributionSourceState& attributionSourceState,
- bool shouldCollectAsyncNotedOp, const String16& message,
- bool shouldCollectMessage) {
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+ const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+ const String16& message, bool shouldCollectMessage) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
- data.writeParcelable(attributionSourceState);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ data.writeString16(attributionTag);
data.writeBool(shouldCollectAsyncNotedOp);
data.writeString16(message);
data.writeBool(shouldCollectMessage);
- remote()->transact(NOTE_OPERATION_WITH_STATE_TRANSACTION, data, &reply);
+ remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
// TODO b/184855056: extract to class
@@ -70,20 +69,22 @@
return reply.readInt32();
}
- virtual int32_t startOperationWithState(const sp<IBinder>& token, int32_t code,
- const AttributionSourceState& attributionSourceState, bool startIfModeDefault,
- bool shouldCollectAsyncNotedOp, const String16& message,
+ virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
+ const String16& packageName, const std::optional<String16>& attributionTag,
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
bool shouldCollectMessage) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
data.writeInt32(code);
- data.writeParcelable(attributionSourceState);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ data.writeString16(attributionTag);
data.writeBool(startIfModeDefault);
data.writeBool(shouldCollectAsyncNotedOp);
data.writeString16(message);
data.writeBool(shouldCollectMessage);
- remote()->transact(START_OPERATION_WITH_STATE_TRANSACTION, data, &reply);
+ remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
// TODO b/184855056: extract to class
@@ -92,14 +93,16 @@
return reply.readInt32();
}
- virtual void finishOperationWithState(const sp<IBinder>& token, int32_t code,
- const AttributionSourceState& attributionSourceState) {
+ virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
+ const String16& packageName, const std::optional<String16>& attributionTag) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
data.writeInt32(code);
- data.writeParcelable(attributionSourceState);
- remote()->transact(FINISH_OPERATION_WITH_STATE_TRANSACTION, data, &reply);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ data.writeString16(attributionTag);
+ remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
}
virtual void startWatchingMode(int32_t op, const String16& packageName,
@@ -186,65 +189,59 @@
{
//printf("AppOpsService received: "); data.print();
switch(code) {
- case CHECK_OPERATION_WITH_STATE_TRANSACTION: {
+ case CHECK_OPERATION_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
int32_t code = data.readInt32();
- AttributionSourceState attributionSourceState;
- status_t status = data.readParcelable(&attributionSourceState);
- if (status != NO_ERROR) {
- return status;
- }
- int32_t res = checkOperationWithState(code, attributionSourceState);
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ int32_t res = checkOperation(code, uid, packageName);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
} break;
- case NOTE_OPERATION_WITH_STATE_TRANSACTION: {
+ case NOTE_OPERATION_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
int32_t code = data.readInt32();
- AttributionSourceState attributionSourceState;
- status_t status = data.readParcelable(&attributionSourceState);
- if (status != NO_ERROR) {
- return status;
- }
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ std::optional<String16> attributionTag;
+ data.readString16(&attributionTag);
bool shouldCollectAsyncNotedOp = data.readBool();
String16 message = data.readString16();
bool shouldCollectMessage = data.readBool();
- int32_t res = noteOperationWithState(code, attributionSourceState,
+ int32_t res = noteOperation(code, uid, packageName, attributionTag,
shouldCollectAsyncNotedOp, message, shouldCollectMessage);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
} break;
- case START_OPERATION_WITH_STATE_TRANSACTION: {
+ case START_OPERATION_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
sp<IBinder> token = data.readStrongBinder();
int32_t code = data.readInt32();
- AttributionSourceState attributionSourceState;
- status_t status = data.readParcelable(&attributionSourceState);
- if (status != NO_ERROR) {
- return status;
- }
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ std::optional<String16> attributionTag;
+ data.readString16(&attributionTag);
bool startIfModeDefault = data.readBool();
bool shouldCollectAsyncNotedOp = data.readBool();
String16 message = data.readString16();
bool shouldCollectMessage = data.readBool();
- int32_t res = startOperationWithState(token, code, attributionSourceState,
+ int32_t res = startOperation(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
} break;
- case FINISH_OPERATION_WITH_STATE_TRANSACTION: {
+ case FINISH_OPERATION_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
sp<IBinder> token = data.readStrongBinder();
int32_t code = data.readInt32();
- AttributionSourceState attributionSourceState;
- status_t status = data.readParcelable(&attributionSourceState);
- if (status != NO_ERROR) {
- return status;
- }
- finishOperationWithState(token, code, attributionSourceState);
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ std::optional<String16> attributionTag;
+ data.readString16(&attributionTag);
+ finishOperation(token, code, uid, packageName, attributionTag);
reply->writeNoException();
return NO_ERROR;
} break;
diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h
index a5fdc54..918fcdb 100644
--- a/libs/permission/include/binder/IAppOpsService.h
+++ b/libs/permission/include/binder/IAppOpsService.h
@@ -16,7 +16,6 @@
#pragma once
-#include <android/content/AttributionSourceState.h>
#include <binder/IAppOpsCallback.h>
#include <binder/IInterface.h>
@@ -28,24 +27,23 @@
namespace android {
-using android::content::AttributionSourceState;
-
// ----------------------------------------------------------------------
class IAppOpsService : public IInterface
{
public:
DECLARE_META_INTERFACE(AppOpsService)
- virtual int32_t checkOperationWithState(int32_t code,
- const AttributionSourceState& attributionSourceState) = 0;
- virtual int32_t noteOperationWithState(int32_t code,
- const AttributionSourceState& attributionSourceState, bool shouldCollectAsyncNotedOp,
+
+ virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+ const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message, bool shouldCollectMessage) = 0;
- virtual int32_t startOperationWithState(const sp<IBinder>& token, int32_t code,
- const AttributionSourceState& attributionSourceState, bool startIfModeDefault,
- bool shouldCollectAsyncNotedOp, const String16& message, bool shouldCollectMessage) = 0;
- virtual void finishOperationWithState(const sp<IBinder>& token, int32_t code,
- const AttributionSourceState& attributionSourceState) = 0;
+ virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
+ const String16& packageName, const std::optional<String16>& attributionTag,
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+ bool shouldCollectMessage) = 0;
+ virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
+ const String16& packageName, const std::optional<String16>& attributionTag) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
@@ -58,10 +56,10 @@
int32_t flags, const sp<IAppOpsCallback>& callback) = 0;
enum {
- CHECK_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+50,
- NOTE_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+52,
- START_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+53,
- FINISH_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+54,
+ CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1,
+ START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2,
+ FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 76729ef..b213f9a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -256,6 +256,7 @@
"inputflinger_input_reader_fuzzer",
"inputflinger_blocking_queue_fuzzer",
"inputflinger_input_classifier_fuzzer",
+ "inputflinger_input_dispatcher_fuzzer",
// Java/Kotlin targets
"CtsWindowManagerDeviceWindow",
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 1092bdb..e529bdd 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -31,16 +31,30 @@
args.pointerProperties[0].toolType == ToolType::MOUSE;
}
+bool isHoverAction(int32_t action) {
+ return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
+}
+
+bool isStylusHoverEvent(const NotifyMotionArgs& args) {
+ return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
+}
} // namespace
// --- PointerChoreographer ---
PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
PointerChoreographerPolicyInterface& policy)
- : mNextListener(listener),
+ : mTouchControllerConstructor([this]() REQUIRES(mLock) {
+ return mPolicy.createPointerController(
+ PointerControllerInterface::ControllerType::TOUCH);
+ }),
+ mNextListener(listener),
mPolicy(policy),
mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
- mNotifiedPointerDisplayId(ADISPLAY_ID_NONE) {}
+ mNotifiedPointerDisplayId(ADISPLAY_ID_NONE),
+ mShowTouchesEnabled(false),
+ mStylusPointerIconEnabled(false) {}
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
std::scoped_lock _l(mLock);
@@ -69,8 +83,10 @@
if (isFromMouse(args)) {
return processMouseEventLocked(args);
+ } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
+ processStylusHoverEventLocked(args);
} else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
- return processTouchscreenEventLocked(args);
+ processTouchscreenAndStylusEventLocked(args);
}
return args;
}
@@ -114,12 +130,70 @@
* mouse device keeps moving and unfades the cursor.
* For touch events, we do not need to populate the cursor position.
*/
-NotifyMotionArgs PointerChoreographer::processTouchscreenEventLocked(const NotifyMotionArgs& args) {
+void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
+ if (args.displayId == ADISPLAY_ID_NONE) {
+ return;
+ }
+
if (const auto it = mMousePointersByDisplay.find(args.displayId);
it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
it->second->fade(PointerControllerInterface::Transition::GRADUAL);
}
- return args;
+
+ if (!mShowTouchesEnabled) {
+ return;
+ }
+
+ // Get the touch pointer controller for the device, or create one if it doesn't exist.
+ auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
+
+ PointerControllerInterface& pc = *it->second;
+
+ const PointerCoords* coords = args.pointerCoords.data();
+ const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
+ const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
+ std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
+ BitSet32 idBits;
+ if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
+ continue;
+ }
+ uint32_t id = args.pointerProperties[i].id;
+ idToIndex[id] = i;
+ idBits.markBit(id);
+ }
+ }
+ // The PointerController already handles setting spots per-display, so
+ // we do not need to manually manage display changes for touch spots for now.
+ pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
+}
+
+void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
+ if (args.displayId == ADISPLAY_ID_NONE) {
+ return;
+ }
+
+ if (args.getPointerCount() != 1) {
+ LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
+ << args.dump();
+ }
+
+ // Get the stylus pointer controller for the device, or create one if it doesn't exist.
+ auto [it, _] =
+ mStylusPointersByDevice.try_emplace(args.deviceId,
+ getStylusControllerConstructor(args.displayId));
+
+ PointerControllerInterface& pc = *it->second;
+
+ const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+ pc.setPosition(x, y);
+ if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
+ } else {
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
}
void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
@@ -135,9 +209,17 @@
}
void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ processDeviceReset(args);
+
mNextListener.notify(args);
}
+void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
+ std::scoped_lock _l(mLock);
+ mTouchPointersByDevice.erase(args.deviceId);
+ mStylusPointersByDevice.erase(args.deviceId);
+}
+
void PointerChoreographer::notifyPointerCaptureChanged(
const NotifyPointerCaptureChangedArgs& args) {
if (args.request.enable) {
@@ -153,12 +235,25 @@
std::scoped_lock _l(mLock);
dump += "PointerChoreographer:\n";
+ dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
+ dump += StringPrintf("stylus pointer icon enabled: %s\n",
+ mStylusPointerIconEnabled ? "true" : "false");
dump += INDENT "MousePointerControllers:\n";
for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
}
+ dump += INDENT "TouchPointerControllers:\n";
+ for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
+ std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
+ dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
+ }
+ dump += INDENT "StylusPointerControllers:\n";
+ for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
+ std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
+ dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
+ }
dump += "\n";
}
@@ -175,8 +270,16 @@
return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
}
+InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
+ auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
+ [deviceId](const auto& info) { return info.getId() == deviceId; });
+ return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
+}
+
void PointerChoreographer::updatePointerControllersLocked() {
std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
+ std::set<DeviceId> touchDevicesToKeep;
+ std::set<DeviceId> stylusDevicesToKeep;
// Mark the displayIds or deviceIds of PointerControllers currently needed.
for (const auto& info : mInputDeviceInfos) {
@@ -187,17 +290,25 @@
getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
mouseDisplaysToKeep.insert(resolvedDisplayId);
}
+ if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
+ info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+ touchDevicesToKeep.insert(info.getId());
+ }
+ if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
+ info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+ stylusDevicesToKeep.insert(info.getId());
+ }
}
// Remove PointerControllers no longer needed.
- // This has the side-effect of fading pointers or clearing spots before removal.
std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
- auto& [displayId, controller] = pair;
- if (mouseDisplaysToKeep.find(displayId) == mouseDisplaysToKeep.end()) {
- controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
- return true;
- }
- return false;
+ return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
+ });
+ std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
+ return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
+ });
+ std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
+ return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
});
// Notify the policy if there's a change on the pointer display ID.
@@ -234,10 +345,17 @@
void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
std::scoped_lock _l(mLock);
for (const auto& viewport : viewports) {
- if (const auto it = mMousePointersByDisplay.find(viewport.displayId);
+ const int32_t displayId = viewport.displayId;
+ if (const auto it = mMousePointersByDisplay.find(displayId);
it != mMousePointersByDisplay.end()) {
it->second->setDisplayViewport(viewport);
}
+ for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
+ const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
+ if (info && info->getAssociatedDisplayId() == displayId) {
+ stylusPointerController->setDisplayViewport(viewport);
+ }
+ }
}
mViewports = viewports;
notifyPointerDisplayIdChangedLocked();
@@ -263,6 +381,24 @@
return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
}
+void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
+ std::scoped_lock _l(mLock);
+ if (mShowTouchesEnabled == enabled) {
+ return;
+ }
+ mShowTouchesEnabled = enabled;
+ updatePointerControllersLocked();
+}
+
+void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
+ std::scoped_lock _l(mLock);
+ if (mStylusPointerIconEnabled == enabled) {
+ return;
+ }
+ mStylusPointerIconEnabled = enabled;
+ updatePointerControllersLocked();
+}
+
PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
int32_t displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
@@ -277,4 +413,18 @@
return ConstructorDelegate(std::move(ctor));
}
+PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
+ int32_t displayId) {
+ std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
+ [this, displayId]() REQUIRES(mLock) {
+ auto pc = mPolicy.createPointerController(
+ PointerControllerInterface::ControllerType::STYLUS);
+ if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
+ pc->setDisplayViewport(*viewport);
+ }
+ return pc;
+ };
+ return ConstructorDelegate(std::move(ctor));
+}
+
} // namespace android
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index c1b900f..26d2fef 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -56,6 +56,8 @@
virtual std::optional<DisplayViewport> getViewportForPointerDevice(
int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
virtual FloatPoint getMouseCursorPosition(int32_t displayId) = 0;
+ virtual void setShowTouchesEnabled(bool enabled) = 0;
+ virtual void setStylusPointerIconEnabled(bool enabled) = 0;
/**
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
@@ -73,6 +75,8 @@
std::optional<DisplayViewport> getViewportForPointerDevice(
int32_t associatedDisplayId) override;
FloatPoint getMouseCursorPosition(int32_t displayId) override;
+ void setShowTouchesEnabled(bool enabled) override;
+ void setStylusPointerIconEnabled(bool enabled) override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
@@ -91,14 +95,19 @@
void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock);
const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);
+ InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
- NotifyMotionArgs processTouchscreenEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ void processDeviceReset(const NotifyDeviceResetArgs& args);
using ControllerConstructor =
ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
+ ControllerConstructor mTouchControllerConstructor GUARDED_BY(mLock);
ControllerConstructor getMouseControllerConstructor(int32_t displayId) REQUIRES(mLock);
+ ControllerConstructor getStylusControllerConstructor(int32_t displayId) REQUIRES(mLock);
std::mutex mLock;
@@ -107,11 +116,17 @@
std::map<int32_t, std::shared_ptr<PointerControllerInterface>> mMousePointersByDisplay
GUARDED_BY(mLock);
+ std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mTouchPointersByDevice
+ GUARDED_BY(mLock);
+ std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice
+ GUARDED_BY(mLock);
int32_t mDefaultMouseDisplayId GUARDED_BY(mLock);
int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock);
std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
+ bool mShowTouchesEnabled GUARDED_BY(mLock);
+ bool mStylusPointerIconEnabled GUARDED_BY(mLock);
};
} // namespace android
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 6f092a6..513cbfa 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -148,7 +148,7 @@
]
}
],
- "hwasan-postsubmit": [
+ "postsubmit": [
{
"name": "CtsWindowManagerDeviceWindow",
"options": [
@@ -281,6 +281,9 @@
"include-filter": "android.security.cts.Poc19_03#testPocBug_115739809"
}
]
+ },
+ {
+ "name": "CtsInputHostTestCases"
}
]
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 188d5f0..5ae3715 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -20,10 +20,12 @@
#include <binder/Binder.h>
#include <gui/constants.h>
#include "../dispatcher/InputDispatcher.h"
+#include "../tests/FakeApplicationHandle.h"
+#include "../tests/FakeInputDispatcherPolicy.h"
+#include "../tests/FakeWindowHandle.h"
using android::base::Result;
using android::gui::WindowInfo;
-using android::gui::WindowInfoHandle;
using android::os::IInputConstants;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
@@ -33,171 +35,17 @@
namespace {
// An arbitrary device id.
-constexpr int32_t DEVICE_ID = 1;
+constexpr DeviceId DEVICE_ID = 1;
-// The default pid and uid for windows created by the test.
-constexpr gui::Pid WINDOW_PID{999};
-constexpr gui::Uid WINDOW_UID{1001};
+// An arbitrary display id
+constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
-static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
static nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-// --- FakeInputDispatcherPolicy ---
-
-class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
-public:
- FakeInputDispatcherPolicy() = default;
- virtual ~FakeInputDispatcherPolicy() = default;
-
-private:
- void notifyConfigurationChanged(nsecs_t) override {}
-
- void notifyNoFocusedWindowAnr(
- const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
- ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
- }
-
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
- const std::string& reason) override {
- ALOGE("Window is not responding: %s", reason.c_str());
- }
-
- void notifyWindowResponsive(const sp<IBinder>& connectionToken,
- std::optional<gui::Pid> pid) override {}
-
- void notifyInputChannelBroken(const sp<IBinder>&) override {}
-
- void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
-
- void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
- InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
- const std::vector<float>& values) override {}
-
- void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
- InputDeviceSensorAccuracy accuracy) override {}
-
- void notifyVibratorState(int32_t deviceId, bool isOn) override {}
-
- bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
- return true; // dispatch event normally
- }
-
- void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}
-
- void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
-
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
- return 0;
- }
-
- std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
- uint32_t) override {
- return {};
- }
-
- void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
-
- void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
-
- void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
-
- void setPointerCapture(const PointerCaptureRequest&) override {}
-
- void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
-
- void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<gui::Uid>& uids) override {}
-
- InputDispatcherConfiguration mConfig;
-};
-
-class FakeApplicationHandle : public InputApplicationHandle {
-public:
- FakeApplicationHandle() {}
- virtual ~FakeApplicationHandle() {}
-
- virtual bool updateInfo() {
- mInfo.dispatchingTimeoutMillis =
- std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
- return true;
- }
-};
-
-class FakeInputReceiver {
-public:
- void consumeEvent() {
- uint32_t consumeSeq = 0;
- InputEvent* event;
-
- std::chrono::time_point start = std::chrono::steady_clock::now();
- status_t result = WOULD_BLOCK;
- while (result == WOULD_BLOCK) {
- std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
- if (elapsed > 10ms) {
- ALOGE("Waited too long for consumer to produce an event, giving up");
- break;
- }
- result = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
- &event);
- }
- if (result != OK) {
- ALOGE("Received result = %d from consume()", result);
- }
- result = mConsumer->sendFinishedSignal(consumeSeq, true);
- if (result != OK) {
- ALOGE("Received result = %d from sendFinishedSignal", result);
- }
- }
-
-protected:
- explicit FakeInputReceiver(InputDispatcher& dispatcher, const std::string name) {
- Result<std::unique_ptr<InputChannel>> channelResult = dispatcher.createInputChannel(name);
- LOG_ALWAYS_FATAL_IF(!channelResult.ok());
- mClientChannel = std::move(*channelResult);
- mConsumer = std::make_unique<InputConsumer>(mClientChannel);
- }
-
- virtual ~FakeInputReceiver() {}
-
- std::shared_ptr<InputChannel> mClientChannel;
- std::unique_ptr<InputConsumer> mConsumer;
- PreallocatedInputEventFactory mEventFactory;
-};
-
-class FakeWindowHandle : public WindowInfoHandle, public FakeInputReceiver {
-public:
- static const int32_t WIDTH = 200;
- static const int32_t HEIGHT = 200;
-
- FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- InputDispatcher& dispatcher, const std::string name)
- : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
- inputApplicationHandle->updateInfo();
- updateInfo();
- mInfo.applicationInfo = *inputApplicationHandle->getInfo();
- }
-
- void updateInfo() {
- mInfo.token = mClientChannel->getConnectionToken();
- mInfo.name = "FakeWindowHandle";
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.frame = mFrame;
- mInfo.globalScaleFactor = 1.0;
- mInfo.touchableRegion.clear();
- mInfo.addTouchableRegion(mFrame);
- mInfo.ownerPid = WINDOW_PID;
- mInfo.ownerUid = WINDOW_UID;
- mInfo.displayId = ADISPLAY_ID_DEFAULT;
- }
-
-protected:
- Rect mFrame;
-};
-
static MotionEvent generateMotionEvent() {
PointerProperties pointerProperties[1];
PointerCoords pointerCoords[1];
@@ -263,7 +111,7 @@
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
+ sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);
dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -281,8 +129,8 @@
motionArgs.eventTime = now();
dispatcher.notifyMotion(motionArgs);
- window->consumeEvent();
- window->consumeEvent();
+ window->consumeMotion();
+ window->consumeMotion();
}
dispatcher.stop();
@@ -298,7 +146,7 @@
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
+ sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);
dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -315,8 +163,8 @@
INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
- window->consumeEvent();
- window->consumeEvent();
+ window->consumeMotion();
+ window->consumeMotion();
}
dispatcher.stop();
@@ -332,7 +180,7 @@
// Create a window
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
+ sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);
std::vector<gui::WindowInfo> windowInfos{*window->getInfo()};
gui::DisplayInfo info;
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 30e6802..2d1a22b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -67,24 +67,11 @@
injectionState(nullptr),
dispatchInProgress(false) {}
-EventEntry::~EventEntry() {
- releaseInjectionState();
-}
-
-void EventEntry::releaseInjectionState() {
- if (injectionState) {
- injectionState->release();
- injectionState = nullptr;
- }
-}
-
// --- ConfigurationChangedEntry ---
ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime)
: EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
-ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
-
std::string ConfigurationChangedEntry::getDescription() const {
return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
}
@@ -94,8 +81,6 @@
DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId)
: EventEntry(id, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
-DeviceResetEntry::~DeviceResetEntry() {}
-
std::string DeviceResetEntry::getDescription() const {
return StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
}
@@ -110,8 +95,6 @@
hasFocus(hasFocus),
reason(reason) {}
-FocusEntry::~FocusEntry() {}
-
std::string FocusEntry::getDescription() const {
return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
}
@@ -125,8 +108,6 @@
: EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
pointerCaptureRequest(request) {}
-PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
-
std::string PointerCaptureChangedEntry::getDescription() const {
return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
pointerCaptureRequest.enable ? "true" : "false");
@@ -143,18 +124,16 @@
x(x),
y(y) {}
-DragEntry::~DragEntry() {}
-
std::string DragEntry::getDescription() const {
return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
}
// --- KeyEntry ---
-KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
- nsecs_t downTime)
+KeyEntry::KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, int32_t repeatCount, nsecs_t downTime)
: EventEntry(id, Type::KEY, eventTime, policyFlags),
deviceId(deviceId),
source(source),
@@ -168,9 +147,9 @@
downTime(downTime),
syntheticRepeat(false),
interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN),
- interceptKeyWakeupTime(0) {}
-
-KeyEntry::~KeyEntry() {}
+ interceptKeyWakeupTime(0) {
+ EventEntry::injectionState = std::move(injectionState);
+}
std::string KeyEntry::getDescription() const {
if (!IS_DEBUGGABLE_BUILD) {
@@ -185,15 +164,6 @@
keyCode, scanCode, metaState, repeatCount, policyFlags);
}
-void KeyEntry::recycle() {
- releaseInjectionState();
-
- dispatchInProgress = false;
- syntheticRepeat = false;
- interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
- interceptKeyWakeupTime = 0;
-}
-
// --- TouchModeEntry ---
TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId)
@@ -201,21 +171,19 @@
inTouchMode(inTouchMode),
displayId(displayId) {}
-TouchModeEntry::~TouchModeEntry() {}
-
std::string TouchModeEntry::getDescription() const {
return StringPrintf("TouchModeEvent(inTouchMode=%s)", inTouchMode ? "true" : "false");
}
// --- MotionEntry ---
-MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState,
- int32_t buttonState, MotionClassification classification,
- int32_t edgeFlags, float xPrecision, float yPrecision,
- float xCursorPosition, float yCursorPosition, nsecs_t downTime,
- const std::vector<PointerProperties>& pointerProperties,
+MotionEntry::MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState,
+ nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+ uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
+ int32_t metaState, int32_t buttonState,
+ MotionClassification classification, int32_t edgeFlags, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition,
+ nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties,
const std::vector<PointerCoords>& pointerCoords)
: EventEntry(id, Type::MOTION, eventTime, policyFlags),
deviceId(deviceId),
@@ -234,9 +202,9 @@
yCursorPosition(yCursorPosition),
downTime(downTime),
pointerProperties(pointerProperties),
- pointerCoords(pointerCoords) {}
-
-MotionEntry::~MotionEntry() {}
+ pointerCoords(pointerCoords) {
+ EventEntry::injectionState = std::move(injectionState);
+}
std::string MotionEntry::getDescription() const {
if (!IS_DEBUGGABLE_BUILD) {
@@ -285,8 +253,6 @@
hwTimestamp(hwTimestamp),
values(std::move(values)) {}
-SensorEntry::~SensorEntry() {}
-
std::string SensorEntry::getDescription() const {
std::string msg;
msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, "
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index b341784..d44a211 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -48,7 +48,7 @@
Type type;
nsecs_t eventTime;
uint32_t policyFlags;
- InjectionState* injectionState;
+ std::shared_ptr<InjectionState> injectionState;
bool dispatchInProgress; // initially false, set to true while dispatching
@@ -72,17 +72,12 @@
virtual std::string getDescription() const = 0;
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
- virtual ~EventEntry();
-
-protected:
- void releaseInjectionState();
+ virtual ~EventEntry() = default;
};
struct ConfigurationChangedEntry : EventEntry {
explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
std::string getDescription() const override;
-
- ~ConfigurationChangedEntry() override;
};
struct DeviceResetEntry : EventEntry {
@@ -90,8 +85,6 @@
DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
std::string getDescription() const override;
-
- ~DeviceResetEntry() override;
};
struct FocusEntry : EventEntry {
@@ -102,8 +95,6 @@
FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
const std::string& reason);
std::string getDescription() const override;
-
- ~FocusEntry() override;
};
struct PointerCaptureChangedEntry : EventEntry {
@@ -111,8 +102,6 @@
PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
std::string getDescription() const override;
-
- ~PointerCaptureChangedEntry() override;
};
struct DragEntry : EventEntry {
@@ -123,8 +112,6 @@
DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
float y);
std::string getDescription() const override;
-
- ~DragEntry() override;
};
struct KeyEntry : EventEntry {
@@ -150,13 +137,11 @@
InterceptKeyResult interceptKeyResult; // set based on the interception result
nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
- KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
- int32_t metaState, int32_t repeatCount, nsecs_t downTime);
+ KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+ int32_t repeatCount, nsecs_t downTime);
std::string getDescription() const override;
- void recycle();
-
- ~KeyEntry() override;
};
struct MotionEntry : EventEntry {
@@ -180,16 +165,14 @@
size_t getPointerCount() const { return pointerProperties.size(); }
- MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
- int32_t metaState, int32_t buttonState, MotionClassification classification,
- int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime,
- const std::vector<PointerProperties>& pointerProperties,
+ MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, int32_t edgeFlags,
+ float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition,
+ nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties,
const std::vector<PointerCoords>& pointerCoords);
std::string getDescription() const override;
-
- ~MotionEntry() override;
};
std::ostream& operator<<(std::ostream& out, const MotionEntry& motionEntry);
@@ -209,8 +192,6 @@
InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
std::vector<float> values);
std::string getDescription() const override;
-
- ~SensorEntry() override;
};
struct TouchModeEntry : EventEntry {
@@ -219,8 +200,6 @@
TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int32_t displayId);
std::string getDescription() const override;
-
- ~TouchModeEntry() override;
};
// Tracks the progress of dispatching a particular event to a particular connection.
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index 053594b..f693dcf 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -20,22 +20,10 @@
namespace android::inputdispatcher {
-InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid)
- : refCount(1),
- targetUid(targetUid),
+InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid, bool isAsync)
+ : targetUid(targetUid),
+ injectionIsAsync(isAsync),
injectionResult(android::os::InputEventInjectionResult::PENDING),
- injectionIsAsync(false),
pendingForegroundDispatches(0) {}
-InjectionState::~InjectionState() {}
-
-void InjectionState::release() {
- refCount -= 1;
- if (refCount == 0) {
- delete this;
- } else {
- ALOG_ASSERT(refCount > 0);
- }
-}
-
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 3a3f5ae..8225dec 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -24,18 +24,12 @@
namespace inputdispatcher {
struct InjectionState {
- mutable int32_t refCount;
-
- std::optional<gui::Uid> targetUid;
+ const std::optional<gui::Uid> targetUid;
+ const bool injectionIsAsync; // set to true if injection is not waiting for the result
android::os::InputEventInjectionResult injectionResult; // initially PENDING
- bool injectionIsAsync; // set to true if injection is not waiting for the result
int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
- explicit InjectionState(const std::optional<gui::Uid>& targetUid);
- void release();
-
-private:
- ~InjectionState();
+ explicit InjectionState(const std::optional<gui::Uid>& targetUid, bool isAsync);
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 156697a..1958c35 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -392,21 +392,17 @@
}
std::unique_ptr<MotionEntry> combinedMotionEntry =
- std::make_unique<MotionEntry>(motionEntry.id, motionEntry.eventTime,
- motionEntry.deviceId, motionEntry.source,
- motionEntry.displayId, motionEntry.policyFlags,
- motionEntry.action, motionEntry.actionButton,
- motionEntry.flags, motionEntry.metaState,
- motionEntry.buttonState, motionEntry.classification,
- motionEntry.edgeFlags, motionEntry.xPrecision,
- motionEntry.yPrecision, motionEntry.xCursorPosition,
- motionEntry.yCursorPosition, motionEntry.downTime,
- motionEntry.pointerProperties, pointerCoords);
-
- if (motionEntry.injectionState) {
- combinedMotionEntry->injectionState = motionEntry.injectionState;
- combinedMotionEntry->injectionState->refCount += 1;
- }
+ std::make_unique<MotionEntry>(motionEntry.id, motionEntry.injectionState,
+ motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId,
+ motionEntry.policyFlags, motionEntry.action,
+ motionEntry.actionButton, motionEntry.flags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime, motionEntry.pointerProperties,
+ pointerCoords);
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
@@ -1492,7 +1488,7 @@
}
void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
- InjectionState* injectionState = entry->injectionState;
+ const std::shared_ptr<InjectionState>& injectionState = entry->injectionState;
if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("Injected inbound event was dropped.");
@@ -1518,10 +1514,11 @@
(POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
std::shared_ptr<KeyEntry> newEntry =
- std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, entry->deviceId,
- entry->source, entry->displayId, policyFlags, entry->action,
- entry->flags, entry->keyCode, entry->scanCode,
- entry->metaState, entry->repeatCount + 1, entry->downTime);
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, entry->deviceId, entry->source,
+ entry->displayId, policyFlags, entry->action, entry->flags,
+ entry->keyCode, entry->scanCode, entry->metaState,
+ entry->repeatCount + 1, entry->downTime);
newEntry->syntheticRepeat = true;
mKeyRepeatState.lastKeyEntry = newEntry;
@@ -4247,7 +4244,8 @@
").",
originalMotionEntry.id, newId));
std::unique_ptr<MotionEntry> splitMotionEntry =
- std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime,
+ std::make_unique<MotionEntry>(newId, originalMotionEntry.injectionState,
+ originalMotionEntry.eventTime,
originalMotionEntry.deviceId, originalMotionEntry.source,
originalMotionEntry.displayId,
originalMotionEntry.policyFlags, action,
@@ -4262,11 +4260,6 @@
originalMotionEntry.yCursorPosition, splitDownTime,
splitPointerProperties, splitPointerCoords);
- if (originalMotionEntry.injectionState) {
- splitMotionEntry->injectionState = originalMotionEntry.injectionState;
- splitMotionEntry->injectionState->refCount += 1;
- }
-
return splitMotionEntry;
}
@@ -4354,9 +4347,10 @@
}
std::unique_ptr<KeyEntry> newEntry =
- std::make_unique<KeyEntry>(args.id, args.eventTime, args.deviceId, args.source,
- args.displayId, policyFlags, args.action, flags, keyCode,
- args.scanCode, metaState, repeatCount, args.downTime);
+ std::make_unique<KeyEntry>(args.id, /*injectionState=*/nullptr, args.eventTime,
+ args.deviceId, args.source, args.displayId, policyFlags,
+ args.action, flags, keyCode, args.scanCode, metaState,
+ repeatCount, args.downTime);
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
@@ -4474,14 +4468,14 @@
// Just enqueue a new motion event.
std::unique_ptr<MotionEntry> newEntry =
- std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source,
- args.displayId, policyFlags, args.action,
- args.actionButton, args.flags, args.metaState,
- args.buttonState, args.classification, args.edgeFlags,
- args.xPrecision, args.yPrecision,
- args.xCursorPosition, args.yCursorPosition,
- args.downTime, args.pointerProperties,
- args.pointerCoords);
+ std::make_unique<MotionEntry>(args.id, /*injectionState=*/nullptr, args.eventTime,
+ args.deviceId, args.source, args.displayId,
+ policyFlags, args.action, args.actionButton,
+ args.flags, args.metaState, args.buttonState,
+ args.classification, args.edgeFlags, args.xPrecision,
+ args.yPrecision, args.xCursorPosition,
+ args.yCursorPosition, args.downTime,
+ args.pointerProperties, args.pointerCoords);
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
@@ -4553,7 +4547,6 @@
}
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
- // TODO(b/308677868) Remove device reset from the InputListener interface
if (debugInboundEventDetails()) {
ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args.eventTime,
args.deviceId);
@@ -4628,6 +4621,9 @@
resolvedDeviceId = event->getDeviceId();
}
+ const bool isAsync = syncMode == InputEventInjectionSync::NONE;
+ auto injectionState = std::make_shared<InjectionState>(targetUid, isAsync);
+
std::queue<std::unique_ptr<EventEntry>> injectedEntries;
switch (event->getType()) {
case InputEventType::KEY: {
@@ -4660,10 +4656,11 @@
mLock.lock();
std::unique_ptr<KeyEntry> injectedEntry =
- std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
- resolvedDeviceId, incomingKey.getSource(),
- incomingKey.getDisplayId(), policyFlags, action,
- flags, keyCode, incomingKey.getScanCode(), metaState,
+ std::make_unique<KeyEntry>(incomingKey.getId(), injectionState,
+ incomingKey.getEventTime(), resolvedDeviceId,
+ incomingKey.getSource(), incomingKey.getDisplayId(),
+ policyFlags, action, flags, keyCode,
+ incomingKey.getScanCode(), metaState,
incomingKey.getRepeatCount(),
incomingKey.getDownTime());
injectedEntries.push(std::move(injectedEntry));
@@ -4695,30 +4692,6 @@
}
mLock.lock();
-
- if (policyFlags & POLICY_FLAG_FILTERED) {
- // The events from InputFilter impersonate real hardware devices. Check these
- // events for consistency and print an error. An inconsistent event sent from
- // InputFilter could cause a crash in the later stages of dispatching pipeline.
- auto [it, _] =
- mInputFilterVerifiersByDisplay
- .try_emplace(displayId,
- StringPrintf("Injection on %" PRId32, displayId));
- InputVerifier& verifier = it->second;
-
- Result<void> result =
- verifier.processMovement(resolvedDeviceId, motionEvent.getSource(),
- motionEvent.getAction(),
- motionEvent.getPointerCount(),
- motionEvent.getPointerProperties(),
- motionEvent.getSamplePointerCoords(), flags);
- if (!result.ok()) {
- logDispatchStateLocked();
- LOG(ERROR) << "Inconsistent event: " << motionEvent
- << ", reason: " << result.error();
- }
- }
-
const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
const size_t pointerCount = motionEvent.getPointerCount();
const std::vector<PointerProperties>
@@ -4727,9 +4700,10 @@
const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords();
std::unique_ptr<MotionEntry> injectedEntry =
- std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
- resolvedDeviceId, motionEvent.getSource(),
- displayId, policyFlags, motionEvent.getAction(),
+ std::make_unique<MotionEntry>(motionEvent.getId(), injectionState,
+ *sampleEventTimes, resolvedDeviceId,
+ motionEvent.getSource(), displayId, policyFlags,
+ motionEvent.getAction(),
motionEvent.getActionButton(), flags,
motionEvent.getMetaState(),
motionEvent.getButtonState(),
@@ -4749,9 +4723,10 @@
sampleEventTimes += 1;
samplePointerCoords += motionEvent.getPointerCount();
std::unique_ptr<MotionEntry> nextInjectedEntry = std::make_unique<
- MotionEntry>(motionEvent.getId(), *sampleEventTimes, resolvedDeviceId,
- motionEvent.getSource(), displayId, policyFlags,
- motionEvent.getAction(), motionEvent.getActionButton(), flags,
+ MotionEntry>(motionEvent.getId(), injectionState, *sampleEventTimes,
+ resolvedDeviceId, motionEvent.getSource(), displayId,
+ policyFlags, motionEvent.getAction(),
+ motionEvent.getActionButton(), flags,
motionEvent.getMetaState(), motionEvent.getButtonState(),
motionEvent.getClassification(), motionEvent.getEdgeFlags(),
motionEvent.getXPrecision(), motionEvent.getYPrecision(),
@@ -4773,14 +4748,6 @@
return InputEventInjectionResult::FAILED;
}
- InjectionState* injectionState = new InjectionState(targetUid);
- if (syncMode == InputEventInjectionSync::NONE) {
- injectionState->injectionIsAsync = true;
- }
-
- injectionState->refCount += 1;
- injectedEntries.back()->injectionState = injectionState;
-
bool needWake = false;
while (!injectedEntries.empty()) {
if (DEBUG_INJECTION) {
@@ -4843,8 +4810,6 @@
}
}
}
-
- injectionState->release();
} // release lock
if (DEBUG_INJECTION) {
@@ -4890,37 +4855,40 @@
void InputDispatcher::setInjectionResult(EventEntry& entry,
InputEventInjectionResult injectionResult) {
- InjectionState* injectionState = entry.injectionState;
- if (injectionState) {
- if (DEBUG_INJECTION) {
- LOG(INFO) << "Setting input event injection result to "
- << ftl::enum_string(injectionResult);
- }
-
- if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
- // Log the outcome since the injector did not wait for the injection result.
- switch (injectionResult) {
- case InputEventInjectionResult::SUCCEEDED:
- ALOGV("Asynchronous input event injection succeeded.");
- break;
- case InputEventInjectionResult::TARGET_MISMATCH:
- ALOGV("Asynchronous input event injection target mismatch.");
- break;
- case InputEventInjectionResult::FAILED:
- ALOGW("Asynchronous input event injection failed.");
- break;
- case InputEventInjectionResult::TIMED_OUT:
- ALOGW("Asynchronous input event injection timed out.");
- break;
- case InputEventInjectionResult::PENDING:
- ALOGE("Setting result to 'PENDING' for asynchronous injection");
- break;
- }
- }
-
- injectionState->injectionResult = injectionResult;
- mInjectionResultAvailable.notify_all();
+ if (!entry.injectionState) {
+ // Not an injected event.
+ return;
}
+
+ InjectionState& injectionState = *entry.injectionState;
+ if (DEBUG_INJECTION) {
+ LOG(INFO) << "Setting input event injection result to "
+ << ftl::enum_string(injectionResult);
+ }
+
+ if (injectionState.injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
+ // Log the outcome since the injector did not wait for the injection result.
+ switch (injectionResult) {
+ case InputEventInjectionResult::SUCCEEDED:
+ ALOGV("Asynchronous input event injection succeeded.");
+ break;
+ case InputEventInjectionResult::TARGET_MISMATCH:
+ ALOGV("Asynchronous input event injection target mismatch.");
+ break;
+ case InputEventInjectionResult::FAILED:
+ ALOGW("Asynchronous input event injection failed.");
+ break;
+ case InputEventInjectionResult::TIMED_OUT:
+ ALOGW("Asynchronous input event injection timed out.");
+ break;
+ case InputEventInjectionResult::PENDING:
+ ALOGE("Setting result to 'PENDING' for asynchronous injection");
+ break;
+ }
+ }
+
+ injectionState.injectionResult = injectionResult;
+ mInjectionResultAvailable.notify_all();
}
void InputDispatcher::transformMotionEntryForInjectionLocked(
@@ -4947,18 +4915,16 @@
}
void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
- InjectionState* injectionState = entry.injectionState;
- if (injectionState) {
- injectionState->pendingForegroundDispatches += 1;
+ if (entry.injectionState) {
+ entry.injectionState->pendingForegroundDispatches += 1;
}
}
void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) {
- InjectionState* injectionState = entry.injectionState;
- if (injectionState) {
- injectionState->pendingForegroundDispatches -= 1;
+ if (entry.injectionState) {
+ entry.injectionState->pendingForegroundDispatches -= 1;
- if (injectionState->pendingForegroundDispatches == 0) {
+ if (entry.injectionState->pendingForegroundDispatches == 0) {
mInjectionSyncFinished.notify_all();
}
}
@@ -6178,7 +6144,7 @@
uint32_t seq, bool handled,
nsecs_t consumeTime) {
// Handle post-event policy actions.
- bool restartEvent;
+ std::unique_ptr<KeyEntry> fallbackKeyEntry;
{ // Start critical section
auto dispatchEntryIt =
@@ -6202,15 +6168,9 @@
}
if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) {
- KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry.eventEntry));
- restartEvent =
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(dispatchEntry.eventEntry));
+ fallbackKeyEntry =
afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
- } else if (dispatchEntry.eventEntry->type == EventEntry::Type::MOTION) {
- MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry.eventEntry));
- restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry,
- motionEntry, handled);
- } else {
- restartEvent = false;
}
} // End critical section: The -LockedInterruptable methods may have released the lock.
@@ -6234,12 +6194,13 @@
}
}
traceWaitQueueLength(*connection);
- if (restartEvent && connection->status == Connection::Status::NORMAL) {
- connection->outboundQueue.emplace_front(std::move(dispatchEntry));
- traceOutboundQueueLength(*connection);
- } else {
- releaseDispatchEntry(std::move(dispatchEntry));
+ if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {
+ const InputTarget target{.inputChannel = connection->inputChannel,
+ .flags = dispatchEntry->targetFlags};
+ enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target,
+ InputTarget::Flags::DISPATCH_AS_IS);
}
+ releaseDispatchEntry(std::move(dispatchEntry));
}
// Start the next dispatch cycle for this connection.
@@ -6424,15 +6385,15 @@
sendWindowResponsiveCommandLocked(connectionToken, pid);
}
-bool InputDispatcher::afterKeyEventLockedInterruptable(
+std::unique_ptr<KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable(
const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
- KeyEntry& keyEntry, bool handled) {
+ const KeyEntry& keyEntry, bool handled) {
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
if (!handled) {
// Report the key as unhandled, since the fallback was not handled.
mReporter->reportUnhandledKey(keyEntry.id);
}
- return false;
+ return {};
}
// Get the fallback key state.
@@ -6492,7 +6453,7 @@
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
}
- return false;
+ return {};
}
// Dispatch the unhandled key to the policy.
@@ -6517,7 +6478,7 @@
if (connection->status != Connection::Status::NORMAL) {
connection->inputState.removeFallbackKey(originalKeyCode);
- return false;
+ return {};
}
// Latch the fallback keycode for this key on an initial down.
@@ -6578,25 +6539,22 @@
}
if (fallback) {
- // Restart the dispatch cycle using the fallback key.
- keyEntry.eventTime = event.getEventTime();
- keyEntry.deviceId = event.getDeviceId();
- keyEntry.source = event.getSource();
- keyEntry.displayId = event.getDisplayId();
- keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
- keyEntry.keyCode = *fallbackKeyCode;
- keyEntry.scanCode = event.getScanCode();
- keyEntry.metaState = event.getMetaState();
- keyEntry.repeatCount = event.getRepeatCount();
- keyEntry.downTime = event.getDownTime();
- keyEntry.syntheticRepeat = false;
-
+ // Return the fallback key that we want dispatched to the channel.
+ std::unique_ptr<KeyEntry> newEntry =
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), keyEntry.injectionState,
+ event.getEventTime(), event.getDeviceId(),
+ event.getSource(), event.getDisplayId(),
+ keyEntry.policyFlags, keyEntry.action,
+ event.getFlags() | AKEY_EVENT_FLAG_FALLBACK,
+ *fallbackKeyCode, event.getScanCode(),
+ event.getMetaState(), event.getRepeatCount(),
+ event.getDownTime());
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("Unhandled key event: Dispatching fallback key. "
"originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
originalKeyCode, *fallbackKeyCode, keyEntry.metaState);
}
- return true; // restart the event
+ return newEntry;
} else {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("Unhandled key event: No fallback key.");
@@ -6606,13 +6564,7 @@
mReporter->reportUnhandledKey(keyEntry.id);
}
}
- return false;
-}
-
-bool InputDispatcher::afterMotionEventLockedInterruptable(
- const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
- MotionEntry& motionEntry, bool handled) {
- return false;
+ return {};
}
void InputDispatcher::traceInboundQueueLengthLocked() {
@@ -6783,7 +6735,6 @@
// Remove the associated touch mode state.
mTouchModePerDisplay.erase(displayId);
mVerifiersByDisplay.erase(displayId);
- mInputFilterVerifiersByDisplay.erase(displayId);
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e428c4e..e9d52cd 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -286,8 +286,7 @@
void transformMotionEntryForInjectionLocked(MotionEntry&,
const ui::Transform& injectedTransform) const
REQUIRES(mLock);
- // Per-display correction of injected events
- std::map</*displayId*/ int32_t, InputVerifier> mInputFilterVerifiersByDisplay GUARDED_BY(mLock);
+
std::condition_variable mInjectionSyncFinished;
void incrementPendingForegroundDispatches(EventEntry& entry);
void decrementPendingForegroundDispatches(EventEntry& entry);
@@ -660,12 +659,10 @@
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay;
- bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
- DispatchEntry& dispatchEntry, KeyEntry& keyEntry,
- bool handled) REQUIRES(mLock);
- bool afterMotionEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
- DispatchEntry& dispatchEntry, MotionEntry& motionEntry,
- bool handled) REQUIRES(mLock);
+ // Returns a fallback KeyEntry that should be sent to the connection, if required.
+ std::unique_ptr<KeyEntry> afterKeyEventLockedInterruptable(
+ const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
+ const KeyEntry& keyEntry, bool handled) REQUIRES(mLock);
// Find touched state and touched window by token.
std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/>
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 09b5186..16cc266 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -421,9 +421,10 @@
if (action == AMOTION_EVENT_ACTION_CANCEL) {
flags |= AMOTION_EVENT_FLAG_CANCELED;
}
- return std::make_unique<MotionEntry>(mIdGenerator.nextId(), eventTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags,
- action, /*actionButton=*/0, flags, AMETA_NONE,
+ return std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ eventTime, memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags, action,
+ /*actionButton=*/0, flags, AMETA_NONE,
/*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
@@ -437,9 +438,10 @@
for (KeyMemento& memento : mKeyMementos) {
if (shouldCancelKey(memento, options)) {
events.push_back(
- std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId,
- memento.policyFlags, AKEY_EVENT_ACTION_UP,
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags,
+ AKEY_EVENT_ACTION_UP,
memento.flags | AKEY_EVENT_FLAG_CANCELED,
memento.keyCode, memento.scanCode, memento.metaState,
/*repeatCount=*/0, memento.downTime));
@@ -498,8 +500,8 @@
| (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
- memento.deviceId, memento.source,
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, memento.deviceId, memento.source,
memento.displayId, memento.policyFlags, action,
/*actionButton=*/0, memento.flags, AMETA_NONE,
/*buttonState=*/0, MotionClassification::NONE,
@@ -539,11 +541,11 @@
flags |= AMOTION_EVENT_FLAG_CANCELED;
}
events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId,
- memento.policyFlags, action, /*actionButton=*/0,
- flags, AMETA_NONE, /*buttonState=*/0,
- MotionClassification::NONE,
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags, action,
+ /*actionButton=*/0, flags, AMETA_NONE,
+ /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
memento.yCursorPosition, memento.downTime,
@@ -564,8 +566,8 @@
: AMOTION_EVENT_ACTION_POINTER_UP |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
- memento.deviceId, memento.source,
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, memento.deviceId, memento.source,
memento.displayId, memento.policyFlags, action,
/*actionButton=*/0,
memento.flags | AMOTION_EVENT_FLAG_CANCELED,
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index bf48804..1c23720 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -21,7 +21,9 @@
#include <android-base/properties.h>
#include <binder/IBinder.h>
#include <gui/InputApplication.h>
+#include <gui/PidUid.h>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include <utils/RefBase.h>
#include <set>
@@ -146,7 +148,7 @@
virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
/* Notifies the policy that there was an input device interaction with apps. */
- virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) = 0;
};
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 8837b25..ef74a55 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -48,8 +48,8 @@
*/
class PointerControllerInterface {
protected:
- PointerControllerInterface() { }
- virtual ~PointerControllerInterface() { }
+ PointerControllerInterface() {}
+ virtual ~PointerControllerInterface() {}
public:
/**
@@ -63,6 +63,10 @@
LEGACY,
// Represents a single mouse pointer.
MOUSE,
+ // Represents multiple touch spots.
+ TOUCH,
+ // Represents a single stylus pointer.
+ STYLUS,
};
/* Dumps the state of the pointer controller. */
@@ -121,7 +125,7 @@
* pressed (not hovering).
*/
virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId) = 0;
+ BitSet32 spotIdBits, int32_t displayId) = 0;
/* Removes all spots. */
virtual void clearSpots() = 0;
diff --git a/services/inputflinger/tests/FakeApplicationHandle.h b/services/inputflinger/tests/FakeApplicationHandle.h
new file mode 100644
index 0000000..2f634d5
--- /dev/null
+++ b/services/inputflinger/tests/FakeApplicationHandle.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/properties.h>
+#include <android/os/IInputConstants.h>
+#include <gui/InputApplication.h>
+
+namespace android {
+
+namespace inputdispatcher {
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+ FakeApplicationHandle() {
+ static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds(
+ android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ android::base::HwTimeoutMultiplier());
+ mInfo.name = "Fake Application";
+ mInfo.token = sp<BBinder>::make();
+ mInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
+ }
+ virtual ~FakeApplicationHandle() {}
+
+ bool updateInfo() override { return true; }
+
+ void setDispatchingTimeout(std::chrono::milliseconds timeout) {
+ mInfo.dispatchingTimeoutMillis = timeout.count();
+ }
+};
+
+} // namespace inputdispatcher
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
new file mode 100644
index 0000000..e9d93af
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include "InputDispatcherPolicyInterface.h"
+
+namespace android {
+
+// --- FakeInputDispatcherPolicy ---
+
+class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
+public:
+ FakeInputDispatcherPolicy() = default;
+ virtual ~FakeInputDispatcherPolicy() = default;
+
+private:
+ void notifyConfigurationChanged(nsecs_t) override {}
+
+ void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
+ LOG(ERROR) << "There is no focused window for " << applicationHandle->getName();
+ }
+
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
+ const std::string& reason) override {
+ LOG(ERROR) << "Window is not responding: " << reason;
+ }
+
+ void notifyWindowResponsive(const sp<IBinder>& connectionToken,
+ std::optional<gui::Pid> pid) override {}
+
+ void notifyInputChannelBroken(const sp<IBinder>&) override {}
+
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+
+ void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+ const std::vector<float>& values) override {}
+
+ void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) override {}
+
+ void notifyVibratorState(int32_t deviceId, bool isOn) override {}
+
+ bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
+ return true; // dispatch event normally
+ }
+
+ void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}
+
+ void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
+ return 0;
+ }
+
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+ uint32_t) override {
+ return {};
+ }
+
+ void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
+
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+
+ void setPointerCapture(const PointerCaptureRequest&) override {}
+
+ void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
+
+ void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) override {}
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index ca517f3..5475594 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -47,6 +47,8 @@
void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) {
mDisplayId = viewport.displayId;
+ setBounds(viewport.logicalLeft, viewport.logicalTop, viewport.logicalRight - 1,
+ viewport.logicalBottom - 1);
}
void FakePointerController::assertPosition(float x, float y) {
@@ -55,6 +57,12 @@
ASSERT_NEAR(y, actualY, 1);
}
+void FakePointerController::assertSpotCount(int32_t displayId, int32_t count) {
+ auto it = mSpotsByDisplay.find(displayId);
+ ASSERT_TRUE(it != mSpotsByDisplay.end()) << "Spots not found for display " << displayId;
+ ASSERT_EQ(static_cast<size_t>(count), it->second.size());
+}
+
bool FakePointerController::isPointerShown() {
return mIsPointerShown;
}
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index c75f6ed..d7e40b3 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -37,6 +37,7 @@
void setDisplayViewport(const DisplayViewport& viewport) override;
void assertPosition(float x, float y);
+ void assertSpotCount(int32_t displayId, int32_t count);
bool isPointerShown();
private:
diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h
new file mode 100644
index 0000000..fe25130
--- /dev/null
+++ b/services/inputflinger/tests/FakeWindowHandle.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include "../dispatcher/InputDispatcher.h"
+
+using android::base::Result;
+using android::gui::Pid;
+using android::gui::TouchOcclusionMode;
+using android::gui::Uid;
+using android::gui::WindowInfo;
+using android::gui::WindowInfoHandle;
+
+namespace android {
+namespace inputdispatcher {
+
+namespace {
+
+// The default pid and uid for windows created by the test.
+constexpr gui::Pid WINDOW_PID{999};
+constexpr gui::Uid WINDOW_UID{1001};
+
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
+
+} // namespace
+
+class FakeInputReceiver {
+public:
+ std::unique_ptr<InputEvent> consumeEvent(std::chrono::milliseconds timeout) {
+ uint32_t consumeSeq = 0;
+ std::unique_ptr<InputEvent> event;
+
+ std::chrono::time_point start = std::chrono::steady_clock::now();
+ status_t result = WOULD_BLOCK;
+ while (result == WOULD_BLOCK) {
+ InputEvent* rawEventPtr = nullptr;
+ result = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
+ &rawEventPtr);
+ event = std::unique_ptr<InputEvent>(rawEventPtr);
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+ if (elapsed > timeout) {
+ if (timeout != 0ms) {
+ LOG(ERROR) << "Waited too long for consumer to produce an event, giving up";
+ }
+ break;
+ }
+ }
+ // Events produced by this factory are owned pointers.
+ if (result != OK) {
+ if (timeout == 0ms) {
+ // This is likely expected. No need to log.
+ } else {
+ LOG(ERROR) << "Received result = " << result << " from consume";
+ }
+ return nullptr;
+ }
+ result = mConsumer.sendFinishedSignal(consumeSeq, true);
+ if (result != OK) {
+ LOG(ERROR) << "Received result = " << result << " from sendFinishedSignal";
+ }
+ return event;
+ }
+
+ explicit FakeInputReceiver(std::unique_ptr<InputChannel> channel, const std::string name)
+ : mConsumer(std::move(channel)) {}
+
+ virtual ~FakeInputReceiver() {}
+
+private:
+ std::unique_ptr<InputChannel> mClientChannel;
+ InputConsumer mConsumer;
+ DynamicInputEventFactory mEventFactory;
+};
+
+class FakeWindowHandle : public WindowInfoHandle {
+public:
+ static const int32_t WIDTH = 600;
+ static const int32_t HEIGHT = 800;
+
+ FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ InputDispatcher& dispatcher, const std::string name, int32_t displayId)
+ : mName(name) {
+ Result<std::unique_ptr<InputChannel>> channel = dispatcher.createInputChannel(name);
+ mInfo.token = (*channel)->getConnectionToken();
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+
+ mInfo.id = sId++;
+ mInfo.name = name;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.alpha = 1.0;
+ mInfo.frame.left = 0;
+ mInfo.frame.top = 0;
+ mInfo.frame.right = WIDTH;
+ mInfo.frame.bottom = HEIGHT;
+ mInfo.transform.set(0, 0);
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.ownerPid = WINDOW_PID;
+ mInfo.ownerUid = WINDOW_UID;
+ mInfo.displayId = displayId;
+ mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
+ }
+
+ sp<FakeWindowHandle> clone(int32_t displayId) {
+ sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)");
+ handle->mInfo = mInfo;
+ handle->mInfo.displayId = displayId;
+ handle->mInfo.id = sId++;
+ handle->mInputReceiver = mInputReceiver;
+ return handle;
+ }
+
+ void setTouchable(bool touchable) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable);
+ }
+
+ void setFocusable(bool focusable) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable);
+ }
+
+ void setVisible(bool visible) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+ }
+
+ void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+ mInfo.dispatchingTimeout = timeout;
+ }
+
+ void setPaused(bool paused) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::PAUSE_DISPATCHING, paused);
+ }
+
+ void setPreventSplitting(bool preventSplitting) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::PREVENT_SPLITTING, preventSplitting);
+ }
+
+ void setSlippery(bool slippery) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::SLIPPERY, slippery);
+ }
+
+ void setWatchOutsideTouch(bool watchOutside) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
+ }
+
+ void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); }
+
+ void setInterceptsStylus(bool interceptsStylus) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus);
+ }
+
+ void setDropInput(bool dropInput) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput);
+ }
+
+ void setDropInputIfObscured(bool dropInputIfObscured) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured);
+ }
+
+ void setNoInputChannel(bool noInputChannel) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
+ }
+
+ void setDisableUserActivity(bool disableUserActivity) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
+ }
+
+ void setAlpha(float alpha) { mInfo.alpha = alpha; }
+
+ void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
+
+ void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
+
+ void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) {
+ mInfo.frame.left = frame.left;
+ mInfo.frame.top = frame.top;
+ mInfo.frame.right = frame.right;
+ mInfo.frame.bottom = frame.bottom;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(frame);
+
+ const Rect logicalDisplayFrame = displayTransform.transform(frame);
+ ui::Transform translate;
+ translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top);
+ mInfo.transform = translate * displayTransform;
+ }
+
+ void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
+
+ void setIsWallpaper(bool isWallpaper) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::IS_WALLPAPER, isWallpaper);
+ }
+
+ void setDupTouchToWallpaper(bool hasWallpaper) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
+ }
+
+ void setTrustedOverlay(bool trustedOverlay) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay);
+ }
+
+ void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
+ mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
+ }
+
+ void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
+
+ void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
+
+ std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout) {
+ if (mInputReceiver == nullptr) {
+ return nullptr;
+ }
+ return mInputReceiver->consumeEvent(timeout);
+ }
+
+ void consumeMotion() {
+ std::unique_ptr<InputEvent> event = consume(100ms);
+
+ if (event == nullptr) {
+ LOG(FATAL) << mName << ": expected a MotionEvent, but didn't get one.";
+ return;
+ }
+
+ if (event->getType() != InputEventType::MOTION) {
+ LOG(FATAL) << mName << " expected a MotionEvent, got " << *event;
+ return;
+ }
+ }
+
+ sp<IBinder> getToken() { return mInfo.token; }
+
+ const std::string& getName() { return mName; }
+
+ void setOwnerInfo(Pid ownerPid, Uid ownerUid) {
+ mInfo.ownerPid = ownerPid;
+ mInfo.ownerUid = ownerUid;
+ }
+
+ Pid getPid() const { return mInfo.ownerPid; }
+
+ void destroyReceiver() { mInputReceiver = nullptr; }
+
+private:
+ FakeWindowHandle(std::string name) : mName(name){};
+ const std::string mName;
+ std::shared_ptr<FakeInputReceiver> mInputReceiver;
+ static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
+ friend class sp<FakeWindowHandle>;
+};
+
+std::atomic<int32_t> FakeWindowHandle::sId{1};
+
+} // namespace inputdispatcher
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 2509c60..91eceb0 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,6 +16,7 @@
#include "../dispatcher/InputDispatcher.h"
#include "../BlockingQueue.h"
+#include "FakeApplicationHandle.h"
#include "TestEventMatchers.h"
#include <NotifyArgsBuilders.h>
@@ -335,7 +336,7 @@
std::optional<sp<IBinder>> receivedToken =
getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock,
mNotifyInputChannelBroken);
- ASSERT_TRUE(receivedToken.has_value());
+ ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token";
ASSERT_EQ(token, *receivedToken);
}
@@ -366,6 +367,30 @@
ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms));
}
+ void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler) {
+ std::scoped_lock lock(mLock);
+ mUnhandledKeyHandler = handler;
+ }
+
+ void assertUnhandledKeyReported(int32_t keycode) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<int32_t> unhandledKeycode =
+ getItemFromStorageLockedInterruptible(100ms, mReportedUnhandledKeycodes, lock,
+ mNotifyUnhandledKey);
+ ASSERT_TRUE(unhandledKeycode) << "Expected unhandled key to be reported";
+ ASSERT_EQ(unhandledKeycode, keycode);
+ }
+
+ void assertUnhandledKeyNotReported() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<int32_t> unhandledKeycode =
+ getItemFromStorageLockedInterruptible(10ms, mReportedUnhandledKeycodes, lock,
+ mNotifyUnhandledKey);
+ ASSERT_FALSE(unhandledKeycode) << "Expected unhandled key NOT to be reported";
+ }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -395,6 +420,10 @@
BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
+ std::condition_variable mNotifyUnhandledKey;
+ std::queue<int32_t> mReportedUnhandledKeycodes GUARDED_BY(mLock);
+ std::function<std::optional<KeyEvent>(const KeyEvent&)> mUnhandledKeyHandler GUARDED_BY(mLock);
+
// All three ANR-related callbacks behave the same way, so we use this generic function to wait
// for a specific container to become non-empty. When the container is non-empty, return the
// first entry from the container and erase it.
@@ -437,7 +466,6 @@
condition.wait_for(lock, timeout,
[&storage]() REQUIRES(mLock) { return !storage.empty(); });
if (storage.empty()) {
- ADD_FAILURE() << "Did not receive the expected callback";
return std::nullopt;
}
T item = storage.front();
@@ -528,9 +556,12 @@
return delay;
}
- std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event,
uint32_t) override {
- return {};
+ std::scoped_lock lock(mLock);
+ mReportedUnhandledKeycodes.emplace(event.getKeyCode());
+ mNotifyUnhandledKey.notify_all();
+ return mUnhandledKeyHandler != nullptr ? mUnhandledKeyHandler(event) : std::nullopt;
}
void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
@@ -821,37 +852,18 @@
android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
android::base::HwTimeoutMultiplier());
-class FakeApplicationHandle : public InputApplicationHandle {
-public:
- FakeApplicationHandle() {
- mInfo.name = "Fake Application";
- mInfo.token = sp<BBinder>::make();
- mInfo.dispatchingTimeoutMillis =
- std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
- }
- virtual ~FakeApplicationHandle() {}
-
- virtual bool updateInfo() override { return true; }
-
- void setDispatchingTimeout(std::chrono::milliseconds timeout) {
- mInfo.dispatchingTimeoutMillis = timeout.count();
- }
-};
-
class FakeInputReceiver {
public:
explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
- : mName(name) {
- mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
- }
+ : mConsumer(std::move(clientChannel)), mName(name) {}
- InputEvent* consume(std::chrono::milliseconds timeout) {
+ InputEvent* consume(std::chrono::milliseconds timeout, bool handled = false) {
InputEvent* event;
std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event);
if (!consumeSeq) {
return nullptr;
}
- finishEvent(*consumeSeq);
+ finishEvent(*consumeSeq, handled);
return event;
}
@@ -867,8 +879,8 @@
std::chrono::time_point start = std::chrono::steady_clock::now();
status_t status = WOULD_BLOCK;
while (status == WOULD_BLOCK) {
- status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
- &event);
+ status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
+ &event);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > timeout) {
break;
@@ -897,13 +909,13 @@
/**
* To be used together with "receiveEvent" to complete the consumption of an event.
*/
- void finishEvent(uint32_t consumeSeq) {
- const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true);
+ void finishEvent(uint32_t consumeSeq, bool handled = true) {
+ const status_t status = mConsumer.sendFinishedSignal(consumeSeq, handled);
ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
- const status_t status = mConsumer->sendTimeline(inputEventId, timeline);
+ const status_t status = mConsumer.sendTimeline(inputEventId, timeline);
ASSERT_EQ(OK, status);
}
@@ -1061,12 +1073,12 @@
<< ": should not have received any events, so consume() should return NULL";
}
- sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
+ sp<IBinder> getToken() { return mConsumer.getChannel()->getConnectionToken(); }
- int getChannelFd() { return mConsumer->getChannel()->getFd().get(); }
+ int getChannelFd() { return mConsumer.getChannel()->getFd().get(); }
-protected:
- std::unique_ptr<InputConsumer> mConsumer;
+private:
+ InputConsumer mConsumer;
PreallocatedInputEventFactory mEventFactory;
std::string mName;
@@ -1209,8 +1221,8 @@
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
- KeyEvent* consumeKey() {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ KeyEvent* consumeKey(bool handled = true) {
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
if (event == nullptr) {
ADD_FAILURE() << "Consume failed : no event";
return nullptr;
@@ -1349,11 +1361,11 @@
mInputReceiver->sendTimeline(inputEventId, timeline);
}
- InputEvent* consume(std::chrono::milliseconds timeout) {
+ InputEvent* consume(std::chrono::milliseconds timeout, bool handled = true) {
if (mInputReceiver == nullptr) {
return nullptr;
}
- return mInputReceiver->consume(timeout);
+ return mInputReceiver->consume(timeout, handled);
}
MotionEvent* consumeMotion() {
@@ -1406,42 +1418,39 @@
class FakeMonitorReceiver {
public:
- FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId) {
- base::Result<std::unique_ptr<InputChannel>> channel =
- dispatcher.createInputMonitor(displayId, name, MONITOR_PID);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
- }
+ FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId)
+ : mInputReceiver(*dispatcher.createInputMonitor(displayId, name, MONITOR_PID), name) {}
- sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+ sp<IBinder> getToken() { return mInputReceiver.getToken(); }
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
- expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+ expectedFlags);
}
std::optional<int32_t> receiveEvent() {
- return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ return mInputReceiver.receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
}
- void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+ void finishEvent(uint32_t consumeSeq) { return mInputReceiver.finishEvent(consumeSeq); }
void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeMotionEvent(
+ mInputReceiver.consumeMotionEvent(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
WithDisplayId(expectedDisplayId),
WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
@@ -1450,20 +1459,20 @@
void consumeMotionPointerDown(int32_t pointerIdx) {
int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
- /*expectedFlags=*/0);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
+ /*expectedFlags=*/0);
}
void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
- mInputReceiver->consumeMotionEvent(matcher);
+ mInputReceiver.consumeMotionEvent(matcher);
}
- MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
+ MotionEvent* consumeMotion() { return mInputReceiver.consumeMotion(); }
- void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+ void assertNoEvents() { mInputReceiver.assertNoEvents(); }
private:
- std::unique_ptr<FakeInputReceiver> mInputReceiver;
+ FakeInputReceiver mInputReceiver;
};
static InputEventInjectionResult injectKey(
@@ -6534,6 +6543,213 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
}
+class InputDispatcherFallbackKeyTest : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApp = std::make_shared<FakeApplicationHandle>();
+
+ mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
+ setFocusedWindow(mWindow);
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeFocusEvent(/*hasFocus=*/true));
+ }
+
+ void setFallback(int32_t keycode) {
+ mFakePolicy->setUnhandledKeyHandler([keycode](const KeyEvent& event) {
+ return KeyEventBuilder(event).keyCode(keycode).build();
+ });
+ }
+
+ void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) {
+ KeyEvent* event = mWindow->consumeKey(handled);
+ ASSERT_NE(event, nullptr) << "Did not receive key event";
+ ASSERT_THAT(*event, matcher);
+ }
+};
+
+TEST_F(InputDispatcherFallbackKeyTest, PolicyNotNotifiedForHandledKey) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/true, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, PolicyNotifiedForUnhandledKey) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, NoFallbackRequestedByPolicy) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+
+ // Since the policy did not request any fallback to be generated, ensure there are no events.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, FallbackDispatchForUnhandledKey) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+
+ // Since the key was not handled, ensure the fallback event was dispatched instead.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, AppHandlesPreviouslyUnhandledKey) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, but handle the fallback.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ // But this time, the app handles the original key.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key is canceled.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, AppDoesNotHandleFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // App does not handle the fallback either, so ensure another fallback is not generated.
+ setFallback(AKEYCODE_C);
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, InconsistentPolicyCancelsFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, so fallback is generated.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, but assume the policy is misbehaving and it
+ // generates an inconsistent fallback to the one from the DOWN event.
+ setFallback(AKEYCODE_C);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key reported before as DOWN is canceled due to the inconsistency.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, so fallback is generated.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // The original key is canceled.
+ mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD)
+ .keyCode(AKEYCODE_A)
+ .addFlag(AKEY_EVENT_FLAG_CANCELED)
+ .build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A),
+ WithFlags(AKEY_EVENT_FLAG_CANCELED)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key is also canceled due to the original key being canceled.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 4bce824..68f5857 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -27,6 +27,9 @@
namespace android {
using ControllerType = PointerControllerInterface::ControllerType;
+using testing::AllOf;
+
+namespace {
// Helpers to std::visit with lambdas.
template <typename... V>
@@ -35,12 +38,18 @@
Visitor(V...) -> Visitor<V...>;
constexpr int32_t DEVICE_ID = 3;
+constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
constexpr int32_t DISPLAY_ID = 5;
constexpr int32_t ANOTHER_DISPLAY_ID = 10;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE)
.axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
.axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20);
+const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200);
+const auto SECOND_TOUCH_POINTER = PointerBuilder(/*id=*/1, ToolType::FINGER).x(200).y(300);
+const auto STYLUS_POINTER = PointerBuilder(/*id=*/0, ToolType::STYLUS).x(100).y(200);
static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source,
int32_t associatedDisplayId) {
@@ -58,52 +67,44 @@
for (auto displayId : displayIds) {
DisplayViewport viewport;
viewport.displayId = displayId;
+ viewport.logicalRight = DISPLAY_WIDTH;
+ viewport.logicalBottom = DISPLAY_HEIGHT;
viewports.push_back(viewport);
}
return viewports;
}
+} // namespace
+
// --- PointerChoreographerTest ---
class PointerChoreographerTest : public testing::Test, public PointerChoreographerPolicyInterface {
protected:
TestInputListener mTestListener;
PointerChoreographer mChoreographer{mTestListener, *this};
- std::list<std::weak_ptr<FakePointerController>> mPointerControllers{};
- std::shared_ptr<PointerControllerInterface> createPointerController(ControllerType type) {
- mLastCreatedControllerType = type;
- std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
- mPointerControllers.emplace_back(pc);
- return pc;
+ std::shared_ptr<FakePointerController> assertPointerControllerCreated(
+ ControllerType expectedType) {
+ EXPECT_TRUE(mLastCreatedController) << "No PointerController was created";
+ auto [type, controller] = std::move(*mLastCreatedController);
+ EXPECT_EQ(expectedType, type);
+ mLastCreatedController.reset();
+ return controller;
}
- void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) {
- mPointerDisplayIdNotified = displayId;
- }
+ void assertPointerControllerNotCreated() { ASSERT_EQ(std::nullopt, mLastCreatedController); }
- void assertPointerControllerCreated(ControllerType type) {
- ASSERT_EQ(type, mLastCreatedControllerType);
- mLastCreatedControllerType.reset();
- }
-
- void assertPointerControllerNotCreated() {
- ASSERT_EQ(std::nullopt, mLastCreatedControllerType);
- }
-
- void assertPointerControllerCount(size_t count) {
- // At first, erase ones which aren't used anymore.
- auto it = mPointerControllers.begin();
- while (it != mPointerControllers.end()) {
- auto pc = it->lock();
- if (!pc) {
- it = mPointerControllers.erase(it);
- continue;
- }
- it++;
- }
-
- ASSERT_EQ(count, mPointerControllers.size());
+ void assertPointerControllerRemoved(const std::shared_ptr<FakePointerController>& pc) {
+ // Ensure that the code under test is not holding onto this PointerController.
+ // While the policy initially creates the PointerControllers, the PointerChoreographer is
+ // expected to manage their lifecycles. Although we may not want to strictly enforce how
+ // the object is managed, in this case, we need to have a way of ensuring that the
+ // corresponding graphical resources have been released by the PointerController, and the
+ // simplest way of checking for that is to just make sure that the PointerControllers
+ // themselves are released by Choreographer when no longer in use. This check is ensuring
+ // that the reference retained by the test is the last one.
+ ASSERT_EQ(1, pc.use_count()) << "Expected PointerChoreographer to release all references "
+ "to this PointerController";
}
void assertPointerDisplayIdNotified(int32_t displayId) {
@@ -114,8 +115,23 @@
void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); }
private:
- std::optional<ControllerType> mLastCreatedControllerType;
+ std::optional<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
+ mLastCreatedController;
std::optional<int32_t> mPointerDisplayIdNotified;
+
+ std::shared_ptr<PointerControllerInterface> createPointerController(
+ ControllerType type) override {
+ EXPECT_FALSE(mLastCreatedController.has_value())
+ << "More than one PointerController created at a time";
+ std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
+ EXPECT_FALSE(pc->isPointerShown());
+ mLastCreatedController = {type, pc};
+ return pc;
+ }
+
+ void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override {
+ mPointerDisplayIdNotified = displayId;
+ }
};
TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
@@ -169,7 +185,6 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
assertPointerControllerNotCreated();
- assertPointerControllerCount(size_t(0));
}
TEST_F(PointerChoreographerTest, WhenMouseEventOccursCreatesPointerController) {
@@ -182,7 +197,6 @@
.displayId(ADISPLAY_ID_NONE)
.build());
assertPointerControllerCreated(ControllerType::MOUSE);
- assertPointerControllerCount(size_t(1));
}
TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) {
@@ -194,12 +208,11 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
- assertPointerControllerCreated(ControllerType::MOUSE);
- assertPointerControllerCount(size_t(1));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
// Remove the mouse.
mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
- assertPointerControllerCount(size_t(0));
+ assertPointerControllerRemoved(pc);
}
TEST_F(PointerChoreographerTest, WhenKeyboardIsAddedDoesNotCreatePointerController) {
@@ -210,23 +223,21 @@
}
TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) {
- // Just adding a viewport should not create a PointerController.
+ // Just adding a viewport or device should not create a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- assertPointerControllerCount(size_t(0));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
assertPointerControllerNotCreated();
// After the mouse emits event, PointerController will be created and viewport will be set.
- mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
mChoreographer.notifyMotion(
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
.pointer(MOUSE_POINTER)
.deviceId(DEVICE_ID)
.displayId(DISPLAY_ID)
.build());
- assertPointerControllerCount(size_t(1));
- assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(DISPLAY_ID, mPointerControllers.back().lock()->getDisplayId());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
}
TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) {
@@ -240,13 +251,12 @@
.deviceId(DEVICE_ID)
.displayId(DISPLAY_ID)
.build());
- assertPointerControllerCount(size_t(1));
- assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(ADISPLAY_ID_NONE, mPointerControllers.back().lock()->getDisplayId());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
// After Choreographer gets viewport, PointerController should also have viewport.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- ASSERT_EQ(DISPLAY_ID, mPointerControllers.back().lock()->getDisplayId());
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
}
TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) {
@@ -263,9 +273,8 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
- assertPointerControllerCount(size_t(1));
- assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(DISPLAY_ID, mPointerControllers.back().lock()->getDisplayId());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
}
TEST_F(PointerChoreographerTest,
@@ -281,14 +290,13 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
- assertPointerControllerCount(size_t(1));
- assertPointerControllerCreated(ControllerType::MOUSE);
- ASSERT_EQ(DISPLAY_ID, mPointerControllers.back().lock()->getDisplayId());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, firstDisplayPc->getDisplayId());
// Change default mouse display. Existing PointerController should be removed.
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerControllerRemoved(firstDisplayPc);
assertPointerControllerNotCreated();
- assertPointerControllerCount(size_t(0));
// New PointerController for the new default display will be created by the motion event.
mChoreographer.notifyMotion(
@@ -297,9 +305,8 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
- assertPointerControllerCreated(ControllerType::MOUSE);
- assertPointerControllerCount(size_t(1));
- ASSERT_EQ(ANOTHER_DISPLAY_ID, mPointerControllers.back().lock()->getDisplayId());
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, secondDisplayPc->getDisplayId());
}
TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) {
@@ -313,6 +320,7 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
}
@@ -327,6 +335,7 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotNotified();
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
@@ -344,12 +353,12 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
- assertPointerControllerCount(size_t(1));
mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
- assertPointerControllerCount(size_t(0));
+ assertPointerControllerRemoved(pc);
}
TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointerDisplayIdChanged) {
@@ -366,12 +375,14 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
// Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified
// before a mouse event.
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerControllerRemoved(firstDisplayPc);
// After a mouse event, pointer display ID will be notified with new default mouse display.
mChoreographer.notifyMotion(
@@ -380,7 +391,663 @@
.deviceId(DEVICE_ID)
.displayId(ADISPLAY_ID_NONE)
.build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID);
}
+TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set bounds and initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ pc->assertPosition(110, 220);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
+}
+
+TEST_F(PointerChoreographerTest,
+ AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
+ // Add two displays and set one to default.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // Add two devices, one unassociated and the other associated with non-default mouse display.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+
+ // Set bounds and initial position for PointerControllers.
+ unassociatedMousePc->setPosition(100, 200);
+ associatedMousePc->setPosition(300, 400);
+
+ // Make NotifyMotionArgs from the associated mouse and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // Check the status of the PointerControllers.
+ unassociatedMousePc->assertPosition(100, 200);
+ ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+ associatedMousePc->assertPosition(310, 420);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+ ASSERT_TRUE(associatedMousePc->isPointerShown());
+
+ // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID),
+ WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420)));
+}
+
+TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set bounds and initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Assume that pointer capture is enabled.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/1,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE_RELATIVE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyPointerCaptureChanged(
+ NotifyPointerCaptureChangedArgs(/*id=*/2, systemTime(SYSTEM_TIME_MONOTONIC),
+ PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+
+ // Notify motion as if pointer capture is enabled.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE_RELATIVE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .x(10)
+ .y(20)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20))
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that there's no update on the PointerController.
+ pc->assertPosition(100, 200);
+ ASSERT_FALSE(pc->isPointerShown());
+
+ // Check x-y cooridnates, displayId and cursor position are not changed.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(10, 20), WithRelativeMotion(10, 20), WithDisplayId(ADISPLAY_ID_NONE),
+ WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION)));
+}
+
+TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set bounds and initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ pc->assertPosition(110, 220);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Enable pointer capture and check if the PointerController hid the pointer.
+ mChoreographer.notifyPointerCaptureChanged(
+ NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
+ PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, WhenShowTouchesEnabledAndDisabledDoesNotCreatePointerController) {
+ // Disable show touches and add a touch device.
+ mChoreographer.setShowTouchesEnabled(false);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ assertPointerControllerNotCreated();
+
+ // Enable show touches. PointerController still should not be created.
+ mChoreographer.setShowTouchesEnabled(true);
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchEventOccursCreatesPointerController) {
+ // Add a touch device and enable show touches.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+
+ // Emit touch event. Now PointerController should be created.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerCreated(ControllerType::TOUCH);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenShowTouchesDisabledAndTouchEventOccursDoesNotCreatePointerController) {
+ // Add a touch device and disable show touches.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(false);
+ assertPointerControllerNotCreated();
+
+ // Emit touch event. Still, PointerController should not be created.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchDeviceIsRemovedRemovesPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+ // Remove the device.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenShowTouchesDisabledRemovesPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+ assertPointerControllerNotCreated();
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+ // Disable show touches.
+ mChoreographer.setShowTouchesEnabled(false);
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpots) {
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+
+ // Emit first pointer down.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ // Emit second pointer down.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .pointer(SECOND_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 2);
+
+ // Emit second pointer up.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_UP |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .pointer(SECOND_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ // Emit first pointer up.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 0);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpotsForStylusEvent) {
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+ DISPLAY_ID)}});
+
+ // Emit down event with stylus properties.
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertSpotCount(DISPLAY_ID, 1);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpotsForTwoDisplays) {
+ mChoreographer.setShowTouchesEnabled(true);
+ // Add two touch devices associated to different displays.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ ANOTHER_DISPLAY_ID)}});
+
+ // Emit touch event with first device.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::TOUCH);
+ firstDisplayPc->assertSpotCount(DISPLAY_ID, 1);
+
+ // Emit touch events with second device.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .pointer(SECOND_TOUCH_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // There should be another PointerController created.
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+ // Check if the spots are set for the second device.
+ secondDisplayPc->assertSpotCount(ANOTHER_DISPLAY_ID, 2);
+
+ // Check if there's no change on the spot of the first device.
+ firstDisplayPc->assertSpotCount(DISPLAY_ID, 1);
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchDeviceIsResetClearsSpots) {
+ // Make sure the PointerController is created and there is a spot.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ // Reset the device and ensure the touch pointer controller was removed.
+ mChoreographer.notifyDeviceReset(NotifyDeviceResetArgs(/*id=*/1, /*eventTime=*/0, DEVICE_ID));
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenStylusPointerIconEnabledAndDisabledDoesNotCreatePointerController) {
+ // Disable stylus pointer icon and add a stylus device.
+ mChoreographer.setStylusPointerIconEnabled(false);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ assertPointerControllerNotCreated();
+
+ // Enable stylus pointer icon. PointerController still should not be created.
+ mChoreographer.setStylusPointerIconEnabled(true);
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusHoverEventOccursCreatesPointerController) {
+ // Add a stylus device and enable stylus pointer icon.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ assertPointerControllerNotCreated();
+
+ // Emit hover event. Now PointerController should be created.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerCreated(ControllerType::STYLUS);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenStylusPointerIconDisabledAndHoverEventOccursDoesNotCreatePointerController) {
+ // Add a stylus device and disable stylus pointer icon.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(false);
+ assertPointerControllerNotCreated();
+
+ // Emit hover event. Still, PointerController should not be created.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusDeviceIsRemovedRemovesPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Remove the device.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusPointerIconDisabledRemovesPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Disable stylus pointer icon.
+ mChoreographer.setStylusPointerIconEnabled(false);
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, SetsViewportForStylusPointerController) {
+ // Set viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Check that displayId is set.
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Check that displayId is unset.
+ ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+
+ // Set viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Check that displayId is set.
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenViewportDoesNotMatchDoesNotSetViewportForStylusPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Check that displayId is unset.
+ ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+
+ // Set viewport which does not match the associated display of the stylus.
+ mChoreographer.setDisplayViewports(createViewports({ANOTHER_DISPLAY_ID}));
+
+ // Check that displayId is still unset.
+ ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) {
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Emit hover enter event. This is for creating PointerController.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Emit hover move event. After bounds are set, PointerController will update the position.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertPosition(150, 250);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Emit hover exit event.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ // Check that the pointer is gone.
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointerForTwoDisplays) {
+ mChoreographer.setStylusPointerIconEnabled(true);
+ // Add two stylus devices associated to different displays.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, ANOTHER_DISPLAY_ID)}});
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+
+ // Emit hover event with first device. This is for creating PointerController.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Emit hover event with second device. This is for creating PointerController.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // There should be another PointerController created.
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Emit hover event with first device.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // Check the pointer of the first device.
+ firstDisplayPc->assertPosition(150, 250);
+ ASSERT_TRUE(firstDisplayPc->isPointerShown());
+
+ // Emit hover event with second device.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(250).y(350))
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // Check the pointer of the second device.
+ secondDisplayPc->assertPosition(250, 350);
+ ASSERT_TRUE(secondDisplayPc->isPointerShown());
+
+ // Check that there's no change on the pointer of the first device.
+ firstDisplayPc->assertPosition(150, 250);
+ ASSERT_TRUE(firstDisplayPc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusDeviceIsResetRemovesPointer) {
+ // Make sure the PointerController is created and there is a pointer.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Reset the device and see the pointer controller was removed.
+ mChoreographer.notifyDeviceReset(NotifyDeviceResetArgs(/*id=*/1, /*eventTime=*/0, DEVICE_ID));
+ assertPointerControllerRemoved(pc);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index ee6ff53..a0a0f5a 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -464,9 +464,32 @@
return WithPointersMatcher(pointers);
}
-MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
- *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
- return arg.keyCode == keyCode;
+/// Key code
+class WithKeyCodeMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithKeyCodeMatcher(int32_t keyCode) : mKeyCode(keyCode) {}
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mKeyCode == args.keyCode;
+ }
+
+ bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+ return mKeyCode == event.getKeyCode();
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with key code " << KeyEvent::getLabel(mKeyCode);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong key code"; }
+
+private:
+ const int32_t mKeyCode;
+};
+
+inline WithKeyCodeMatcher WithKeyCode(int32_t keyCode) {
+ return WithKeyCodeMatcher(keyCode);
}
MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 9313a89..8a4f6f0 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -167,3 +167,17 @@
"LatencyTrackerFuzzer.cpp",
],
}
+
+cc_fuzz {
+ name: "inputflinger_input_dispatcher_fuzzer",
+ defaults: [
+ "inputflinger_fuzz_defaults",
+ "libinputdispatcher_defaults",
+ ],
+ shared_libs: [
+ "libinputreporter",
+ ],
+ srcs: [
+ "InputDispatcherFuzzer.cpp",
+ ],
+}
diff --git a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
new file mode 100644
index 0000000..885820f
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+namespace {
+static constexpr int32_t MAX_RANDOM_POINTERS = 4;
+static constexpr int32_t MAX_RANDOM_DEVICES = 4;
+} // namespace
+
+int getFuzzedMotionAction(FuzzedDataProvider& fdp) {
+ int actionMasked = fdp.PickValueInArray<int>({
+ AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE,
+ AMOTION_EVENT_ACTION_HOVER_ENTER, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AMOTION_EVENT_ACTION_HOVER_EXIT, AMOTION_EVENT_ACTION_CANCEL,
+ // do not inject AMOTION_EVENT_ACTION_OUTSIDE,
+ AMOTION_EVENT_ACTION_SCROLL, AMOTION_EVENT_ACTION_POINTER_DOWN,
+ AMOTION_EVENT_ACTION_POINTER_UP,
+ // do not send buttons until verifier supports them
+ // AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ // AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ });
+ switch (actionMasked) {
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+ const int32_t index = fdp.ConsumeIntegralInRange(0, MAX_RANDOM_POINTERS - 1);
+ const int32_t action =
+ actionMasked | (index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ return action;
+ }
+ default:
+ return actionMasked;
+ }
+}
+
+/**
+ * For now, focus on the 3 main sources.
+ */
+int getFuzzedSource(FuzzedDataProvider& fdp) {
+ return fdp.PickValueInArray<int>({
+ // AINPUT_SOURCE_UNKNOWN,
+ // AINPUT_SOURCE_KEYBOARD,
+ // AINPUT_SOURCE_DPAD,
+ // AINPUT_SOURCE_GAMEPAD,
+ AINPUT_SOURCE_TOUCHSCREEN, AINPUT_SOURCE_MOUSE, AINPUT_SOURCE_STYLUS,
+ // AINPUT_SOURCE_BLUETOOTH_STYLUS,
+ // AINPUT_SOURCE_TRACKBALL,
+ // AINPUT_SOURCE_MOUSE_RELATIVE,
+ // AINPUT_SOURCE_TOUCHPAD,
+ // AINPUT_SOURCE_TOUCH_NAVIGATION,
+ // AINPUT_SOURCE_JOYSTICK,
+ // AINPUT_SOURCE_HDMI,
+ // AINPUT_SOURCE_SENSOR,
+ // AINPUT_SOURCE_ROTARY_ENCODER,
+ // AINPUT_SOURCE_ANY,
+ });
+}
+
+int getFuzzedButtonState(FuzzedDataProvider& fdp) {
+ return fdp.PickValueInArray<int>({
+ 0,
+ // AMOTION_EVENT_BUTTON_PRIMARY,
+ // AMOTION_EVENT_BUTTON_SECONDARY,
+ // AMOTION_EVENT_BUTTON_TERTIARY,
+ // AMOTION_EVENT_BUTTON_BACK,
+ // AMOTION_EVENT_BUTTON_FORWARD,
+ // AMOTION_EVENT_BUTTON_STYLUS_PRIMARY,
+ // AMOTION_EVENT_BUTTON_STYLUS_SECONDARY,
+ });
+}
+
+int32_t getFuzzedFlags(FuzzedDataProvider& fdp, int32_t action) {
+ constexpr std::array<int32_t, 4> FLAGS{
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
+ AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE,
+ };
+
+ int32_t flags = 0;
+ for (size_t i = 0; i < fdp.ConsumeIntegralInRange(size_t(0), FLAGS.size()); i++) {
+ flags |= fdp.PickValueInArray<int32_t>(FLAGS);
+ }
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ if (MotionEvent::getActionMasked(action) == AMOTION_EVENT_ACTION_POINTER_UP) {
+ if (fdp.ConsumeBool()) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ }
+ return flags;
+}
+
+int32_t getFuzzedPointerCount(FuzzedDataProvider& fdp, int32_t action) {
+ switch (MotionEvent::getActionMasked(action)) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_UP: {
+ return 1;
+ }
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_MOVE:
+ return fdp.ConsumeIntegralInRange<int32_t>(1, MAX_RANDOM_POINTERS);
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ return 1;
+ case AMOTION_EVENT_ACTION_SCROLL:
+ return 1;
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+ const uint8_t actionIndex = MotionEvent::getActionIndex(action);
+ const int32_t count =
+ std::max(actionIndex + 1,
+ fdp.ConsumeIntegralInRange<int32_t>(1, MAX_RANDOM_POINTERS));
+ // Need to have at least 2 pointers
+ return std::max(2, count);
+ }
+ case AMOTION_EVENT_ACTION_BUTTON_PRESS:
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+ return 1;
+ }
+ }
+ return 1;
+}
+
+ToolType getToolType(int32_t source) {
+ switch (source) {
+ case AINPUT_SOURCE_TOUCHSCREEN:
+ return ToolType::FINGER;
+ case AINPUT_SOURCE_MOUSE:
+ return ToolType::MOUSE;
+ case AINPUT_SOURCE_STYLUS:
+ return ToolType::STYLUS;
+ }
+ return ToolType::UNKNOWN;
+}
+
+inline nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+NotifyMotionArgs generateFuzzedMotionArgs(IdGenerator& idGenerator, FuzzedDataProvider& fdp,
+ int32_t maxDisplays) {
+ // Create a basic motion event for testing
+ const int32_t source = getFuzzedSource(fdp);
+ const ToolType toolType = getToolType(source);
+ const int32_t action = getFuzzedMotionAction(fdp);
+ const int32_t pointerCount = getFuzzedPointerCount(fdp, action);
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+ for (int i = 0; i < pointerCount; i++) {
+ PointerProperties properties{};
+ properties.id = i;
+ properties.toolType = toolType;
+ pointerProperties.push_back(properties);
+
+ PointerCoords coords{};
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, fdp.ConsumeIntegralInRange<int>(-1000, 1000));
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, fdp.ConsumeIntegralInRange<int>(-1000, 1000));
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1);
+ pointerCoords.push_back(coords);
+ }
+
+ const int32_t displayId = fdp.ConsumeIntegralInRange<int32_t>(0, maxDisplays - 1);
+ const int32_t deviceId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DEVICES - 1);
+
+ // Current time +- 5 seconds
+ const nsecs_t currentTime = now();
+ const nsecs_t downTime =
+ fdp.ConsumeIntegralInRange<nsecs_t>(currentTime - 5E9, currentTime + 5E9);
+ const nsecs_t readTime = downTime;
+ const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(downTime, downTime + 1E9);
+
+ const float cursorX = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
+ const float cursorY = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
+ return NotifyMotionArgs(idGenerator.nextId(), eventTime, readTime, deviceId, source, displayId,
+ POLICY_FLAG_PASS_TO_USER, action,
+ /*actionButton=*/fdp.ConsumeIntegral<int32_t>(),
+ getFuzzedFlags(fdp, action), AMETA_NONE, getFuzzedButtonState(fdp),
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+ pointerProperties.data(), pointerCoords.data(),
+ /*xPrecision=*/0,
+ /*yPrecision=*/0, cursorX, cursorY, downTime,
+ /*videoFrames=*/{});
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
index 3b3ed9b..deb811d 100644
--- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -16,44 +16,16 @@
#include <MapperHelpers.h>
#include <fuzzer/FuzzedDataProvider.h>
+#include "FuzzedInputStream.h"
#include "InputCommonConverter.h"
#include "InputProcessor.h"
namespace android {
-static constexpr int32_t MAX_AXES = 64;
+namespace {
-// Used by two fuzz operations and a bit lengthy, so pulled out into a function.
-NotifyMotionArgs generateFuzzedMotionArgs(FuzzedDataProvider &fdp) {
- // Create a basic motion event for testing
- PointerProperties properties;
- properties.id = 0;
- properties.toolType = getFuzzedToolType(fdp);
- PointerCoords coords;
- coords.clear();
- for (int32_t i = 0; i < fdp.ConsumeIntegralInRange<int32_t>(0, MAX_AXES); i++) {
- coords.setAxisValue(fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeFloatingPoint<float>());
- }
+constexpr int32_t MAX_RANDOM_DISPLAYS = 4;
- const nsecs_t downTime = 2;
- const nsecs_t readTime = downTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8);
- NotifyMotionArgs motionArgs(/*sequenceNum=*/fdp.ConsumeIntegral<uint32_t>(),
- /*eventTime=*/downTime, readTime,
- /*deviceId=*/fdp.ConsumeIntegral<int32_t>(), AINPUT_SOURCE_ANY,
- ADISPLAY_ID_DEFAULT,
- /*policyFlags=*/fdp.ConsumeIntegral<uint32_t>(),
- AMOTION_EVENT_ACTION_DOWN,
- /*actionButton=*/fdp.ConsumeIntegral<int32_t>(),
- /*flags=*/fdp.ConsumeIntegral<int32_t>(), AMETA_NONE,
- /*buttonState=*/fdp.ConsumeIntegral<int32_t>(),
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
- /*pointerCount=*/1, &properties, &coords,
- /*xPrecision=*/fdp.ConsumeFloatingPoint<float>(),
- /*yPrecision=*/fdp.ConsumeFloatingPoint<float>(),
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
- /*videoFrames=*/{});
- return motionArgs;
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
@@ -62,6 +34,7 @@
std::unique_ptr<FuzzInputListener> mFuzzListener = std::make_unique<FuzzInputListener>();
std::unique_ptr<InputProcessorInterface> mClassifier =
std::make_unique<InputProcessor>(*mFuzzListener);
+ IdGenerator idGenerator(IdGenerator::Source::OTHER);
while (fdp.remaining_bytes() > 0) {
fdp.PickValueInArray<std::function<void()>>({
@@ -90,7 +63,8 @@
},
[&]() -> void {
// SendToNextStage_NotifyMotionArgs
- mClassifier->notifyMotion(generateFuzzedMotionArgs(fdp));
+ mClassifier->notifyMotion(
+ generateFuzzedMotionArgs(idGenerator, fdp, MAX_RANDOM_DISPLAYS));
},
[&]() -> void {
// SendToNextStage_NotifySwitchArgs
@@ -108,7 +82,8 @@
},
[&]() -> void {
// InputClassifierConverterTest
- const NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
+ const NotifyMotionArgs motionArgs =
+ generateFuzzedMotionArgs(idGenerator, fdp, MAX_RANDOM_DISPLAYS);
aidl::android::hardware::input::common::MotionEvent motionEvent =
notifyMotionArgsToHalMotionEvent(motionArgs);
},
diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
new file mode 100644
index 0000000..214649c
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "../FakeApplicationHandle.h"
+#include "../FakeInputDispatcherPolicy.h"
+#include "../FakeWindowHandle.h"
+#include "FuzzedInputStream.h"
+#include "dispatcher/InputDispatcher.h"
+#include "input/InputVerifier.h"
+
+namespace android {
+
+using android::base::Result;
+using android::gui::WindowInfo;
+
+namespace inputdispatcher {
+
+namespace {
+
+static constexpr int32_t MAX_RANDOM_DISPLAYS = 4;
+static constexpr int32_t MAX_RANDOM_WINDOWS = 4;
+
+/**
+ * Provide a valid motion stream, to make the fuzzer more effective.
+ */
+class NotifyStreamProvider {
+public:
+ NotifyStreamProvider(FuzzedDataProvider& fdp)
+ : mFdp(fdp), mIdGenerator(IdGenerator::Source::OTHER), mVerifier("Fuzz verifier") {}
+
+ std::optional<NotifyMotionArgs> nextMotion() {
+ NotifyMotionArgs args = generateFuzzedMotionArgs(mIdGenerator, mFdp, MAX_RANDOM_DISPLAYS);
+ const Result<void> result =
+ mVerifier.processMovement(args.deviceId, args.source, args.action,
+ args.getPointerCount(), args.pointerProperties.data(),
+ args.pointerCoords.data(), args.flags);
+ if (result.ok()) {
+ return args;
+ }
+ return {};
+ }
+
+private:
+ FuzzedDataProvider& mFdp;
+
+ IdGenerator mIdGenerator;
+
+ InputVerifier mVerifier;
+};
+
+} // namespace
+
+sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatcher& dispatcher,
+ int32_t displayId) {
+ static size_t windowNumber = 0;
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ std::string windowName = android::base::StringPrintf("Win") + std::to_string(windowNumber++);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, dispatcher, "Fake", displayId);
+
+ const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+
+ window->setFrame(Rect(left, top, left + width, top + height));
+ window->setSlippery(fdp.ConsumeBool());
+ window->setDupTouchToWallpaper(fdp.ConsumeBool());
+ window->setTrustedOverlay(fdp.ConsumeBool());
+ return window;
+}
+
+void randomizeWindows(
+ std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>>& windowsPerDisplay,
+ FuzzedDataProvider& fdp, InputDispatcher& dispatcher) {
+ const int32_t displayId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DISPLAYS - 1);
+ std::vector<sp<FakeWindowHandle>>& windows = windowsPerDisplay[displayId];
+
+ fdp.PickValueInArray<std::function<void()>>({
+ // Add a new window
+ [&]() -> void {
+ if (windows.size() < MAX_RANDOM_WINDOWS) {
+ windows.push_back(generateFuzzedWindow(fdp, dispatcher, displayId));
+ }
+ },
+ // Remove a window
+ [&]() -> void {
+ if (windows.empty()) {
+ return;
+ }
+ const int32_t erasedPosition =
+ fdp.ConsumeIntegralInRange<int32_t>(0, windows.size() - 1);
+
+ windows.erase(windows.begin() + erasedPosition);
+ if (windows.empty()) {
+ windowsPerDisplay.erase(displayId);
+ }
+ },
+ // Could also clone a window, change flags, reposition, etc...
+ })();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ NotifyStreamProvider streamProvider(fdp);
+
+ FakeInputDispatcherPolicy fakePolicy;
+ InputDispatcher dispatcher(fakePolicy);
+ dispatcher.setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
+ // Start InputDispatcher thread
+ dispatcher.start();
+
+ std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>> windowsPerDisplay;
+
+ // Randomly invoke InputDispatcher api's until randomness is exhausted.
+ while (fdp.remaining_bytes() > 0) {
+ fdp.PickValueInArray<std::function<void()>>({
+ [&]() -> void {
+ std::optional<NotifyMotionArgs> motion = streamProvider.nextMotion();
+ if (motion) {
+ dispatcher.notifyMotion(*motion);
+ }
+ },
+ [&]() -> void {
+ // Scramble the windows we currently have
+ randomizeWindows(/*byref*/ windowsPerDisplay, fdp, dispatcher);
+
+ std::vector<WindowInfo> windowInfos;
+ for (const auto& [displayId, windows] : windowsPerDisplay) {
+ for (const sp<FakeWindowHandle>& window : windows) {
+ windowInfos.emplace_back(*window->getInfo());
+ }
+ }
+
+ dispatcher.onWindowInfosChanged(
+ {windowInfos, {}, /*vsyncId=*/0, /*timestamp=*/0});
+ },
+ // Consume on all the windows
+ [&]() -> void {
+ for (const auto& [_, windows] : windowsPerDisplay) {
+ for (const sp<FakeWindowHandle>& window : windows) {
+ // To speed up the fuzzing, don't wait for consumption. If there's an
+ // event pending, this can be consumed on the next call instead.
+ // We also don't care about whether consumption succeeds here, or what
+ // kind of event is returned.
+ window->consume(0ms);
+ }
+ }
+ },
+ })();
+ }
+
+ dispatcher.stop();
+
+ return 0;
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 1a8644e..1a235e9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -96,6 +96,9 @@
// The expected time for the next present
nsecs_t expectedPresentTime{0};
+ // The frameInterval for the next present
+ Fps frameInterval{};
+
// If set, a frame has been scheduled for that time.
std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 370c7cf..40ebf44 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -298,14 +298,14 @@
virtual void finishFrame(GpuCompositionResult&&) = 0;
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
- virtual void postFramebuffer() = 0;
+ virtual void presentFrameAndReleaseLayers() = 0;
virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
virtual bool chooseCompositionStrategy(
std::optional<android::HWComposer::DeviceRequestedChanges>*) = 0;
virtual void applyCompositionStrategy(
const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0;
virtual bool getSkipColorTransform() const = 0;
- virtual FrameFences presentAndGetFrameFences() = 0;
+ virtual FrameFences presentFrame() = 0;
virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
std::vector<LayerFE*> &outLayerRef) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 6cf1d68..de82931 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -59,7 +59,7 @@
std::optional<android::HWComposer::DeviceRequestedChanges>*) override;
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override;
bool getSkipColorTransform() const override;
- compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+ compositionengine::Output::FrameFences presentFrame() override;
void setExpensiveRenderingExpected(bool) override;
void finishFrame(GpuCompositionResult&&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 229a657..d95fbea 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -102,7 +102,7 @@
std::optional<base::unique_fd> composeSurfaces(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&) override;
- void postFramebuffer() override;
+ void presentFrameAndReleaseLayers() override;
void renderCachedSets(const CompositionRefreshArgs&) override;
void cacheClientCompositionRequests(uint32_t) override;
bool canPredictCompositionStrategy(const CompositionRefreshArgs&) override;
@@ -133,7 +133,7 @@
};
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
- compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+ compositionengine::Output::FrameFences presentFrame() override;
virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings() const;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 6cb1e7e..692ed24 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -127,6 +127,9 @@
// The expected time for the next present
nsecs_t expectedPresentTime{0};
+ // The frameInterval for the next present
+ Fps frameInterval{};
+
// Current display brightness
float displayBrightnessNits{-1.f};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index a56fc79..c88fbd6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -118,9 +118,9 @@
base::unique_fd&));
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
- MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
- MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+ MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<compositionengine::LayerFE*>&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index f2acfc9..469fb38 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -255,10 +255,10 @@
const TimePoint hwcValidateStartTime = TimePoint::now();
- if (status_t result =
- hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
- getState().earliestPresentTime,
- getState().expectedPresentTime, outChanges);
+ if (status_t result = hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
+ getState().earliestPresentTime,
+ getState().expectedPresentTime,
+ getState().frameInterval, outChanges);
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
@@ -362,8 +362,8 @@
static_cast<ui::PixelFormat>(clientTargetProperty.clientTargetProperty.pixelFormat));
}
-compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
- auto fences = impl::Output::presentAndGetFrameFences();
+compositionengine::Output::FrameFences Display::presentFrame() {
+ auto fences = impl::Output::presentFrame();
const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
if (mIsDisconnected || !halDisplayIdOpt) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index fa3733b..2ae80de 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -448,7 +448,7 @@
devOptRepaintFlash(refreshArgs);
finishFrame(std::move(result));
- postFramebuffer();
+ presentFrameAndReleaseLayers();
renderCachedSets(refreshArgs);
}
@@ -836,6 +836,7 @@
editState().earliestPresentTime = refreshArgs.earliestPresentTime;
editState().expectedPresentTime = refreshArgs.expectedPresentTime;
+ editState().frameInterval = refreshArgs.frameInterval;
editState().powerCallback = refreshArgs.powerCallback;
compositionengine::OutputLayer* peekThroughLayer = nullptr;
@@ -1152,7 +1153,7 @@
}
}
- postFramebuffer();
+ presentFrameAndReleaseLayers();
std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay);
@@ -1509,7 +1510,7 @@
return false;
}
-void Output::postFramebuffer() {
+void Output::presentFrameAndReleaseLayers() {
ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
@@ -1520,7 +1521,7 @@
auto& outputState = editState();
outputState.dirtyRegion.clear();
- auto frame = presentAndGetFrameFences();
+ auto frame = presentFrame();
mRenderSurface->onPresentDisplayCompleted();
@@ -1590,7 +1591,7 @@
return true;
}
-compositionengine::Output::FrameFences Output::presentAndGetFrameFences() {
+compositionengine::Output::FrameFences Output::presentFrame() {
compositionengine::Output::FrameFences result;
if (getState().usesClientComposition) {
result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence();
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 027004a..4a778d4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -582,7 +582,7 @@
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _))
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _))
.WillOnce(Return(INVALID_OPERATION));
chooseCompositionStrategy(mDisplay.get());
@@ -606,8 +606,8 @@
.WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
- .WillOnce(testing::DoAll(testing::SetArgPointee<4>(mDeviceRequestedChanges),
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
+ .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
Return(NO_ERROR)));
EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
.Times(1);
@@ -659,8 +659,8 @@
.WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(mDeviceRequestedChanges), Return(NO_ERROR)));
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<5>(mDeviceRequestedChanges), Return(NO_ERROR)));
EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
.Times(1);
EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
@@ -867,7 +867,7 @@
}
/*
- * Display::presentAndGetFrameFences()
+ * Display::presentFrame()
*/
using DisplayPresentAndGetFrameFencesTest = DisplayWithLayersTestCommon;
@@ -876,7 +876,7 @@
auto args = getDisplayCreationArgsForGpuVirtualDisplay();
auto gpuDisplay{impl::createDisplay(mCompositionEngine, args)};
- auto result = gpuDisplay->presentAndGetFrameFences();
+ auto result = gpuDisplay->presentFrame();
ASSERT_TRUE(result.presentFence.get());
EXPECT_FALSE(result.presentFence->isValid());
@@ -900,7 +900,7 @@
.WillOnce(Return(layer2Fence));
EXPECT_CALL(mHwComposer, clearReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
- auto result = mDisplay->presentAndGetFrameFences();
+ auto result = mDisplay->presentFrame();
EXPECT_EQ(presentFence, result.presentFence);
@@ -1060,7 +1060,7 @@
}
};
-TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
+TEST_F(DisplayFunctionalTest, presentFrameAndReleaseLayersCriticalCallsAreOrdered) {
InSequence seq;
mDisplay->editState().isEnabled = true;
@@ -1068,7 +1068,7 @@
EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
- mDisplay->postFramebuffer();
+ mDisplay->presentFrameAndReleaseLayers();
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index a44e4be..b2491d8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -54,9 +54,9 @@
MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
- MOCK_METHOD5(getDeviceCompositionChanges,
- status_t(HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>,
- nsecs_t, std::optional<android::HWComposer::DeviceRequestedChanges>*));
+ MOCK_METHOD(status_t, getDeviceCompositionChanges,
+ (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
+ Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD5(setClientTarget,
status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
ui::Dataspace));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index cdcb68d..5537fcd 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2007,7 +2007,7 @@
MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
- MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
};
@@ -2029,7 +2029,7 @@
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(_));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
@@ -2049,7 +2049,7 @@
EXPECT_CALL(mOutput, prepareFrameAsync());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(_));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
@@ -2895,7 +2895,7 @@
std::optional<base::unique_fd>(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&));
- MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
@@ -2932,7 +2932,7 @@
mOutput.mState.isEnabled = false;
InSequence seq;
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2944,7 +2944,7 @@
InSequence seq;
EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2960,7 +2960,7 @@
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2978,7 +2978,7 @@
std::optional<base::unique_fd>(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&));
- MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
@@ -3057,14 +3057,14 @@
}
/*
- * Output::postFramebuffer()
+ * Output::presentFrameAndReleaseLayers()
*/
struct OutputPostFramebufferTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+ MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
};
struct Layer {
@@ -3104,7 +3104,7 @@
TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
mOutput.mState.isEnabled = false;
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
@@ -3119,10 +3119,10 @@
// setup below are satisfied in the specific order.
InSequence seq;
- EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
@@ -3141,7 +3141,7 @@
frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
- EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
// Compare the pointers values of each fence to make sure the correct ones
@@ -3164,7 +3164,7 @@
EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
});
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
@@ -3177,7 +3177,7 @@
frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
- EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
// Fence::merge is called, and since none of the fences are actually valid,
@@ -3187,7 +3187,7 @@
EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
@@ -3212,7 +3212,7 @@
Output::FrameFences frameFences;
frameFences.presentFence = presentFence;
- EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
// Each released layer should be given the presentFence.
@@ -3232,7 +3232,7 @@
EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
});
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
// After the call the list of released layers should have been cleared.
EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 1643ad0..2a6443a 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -749,7 +749,8 @@
}
Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests) {
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
const auto displayId = translate<int64_t>(display);
ATRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId);
@@ -758,7 +759,8 @@
auto writer = getWriter(display);
auto reader = getReader(display);
if (writer && reader) {
- writer->get().validateDisplay(displayId, ClockMonotonicTimestamp{expectedPresentTime});
+ writer->get().validateDisplay(displayId, ClockMonotonicTimestamp{expectedPresentTime},
+ frameIntervalNs);
error = execute(display);
} else {
error = Error::BAD_DISPLAY;
@@ -776,8 +778,9 @@
}
Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) {
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) {
const auto displayId = translate<int64_t>(display);
ATRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId);
@@ -787,7 +790,8 @@
auto reader = getReader(display);
if (writer && reader) {
writer->get().presentOrvalidateDisplay(displayId,
- ClockMonotonicTimestamp{expectedPresentTime});
+ ClockMonotonicTimestamp{expectedPresentTime},
+ frameIntervalNs);
error = execute(display);
} else {
error = Error::BAD_DISPLAY;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 7693a80..1635a16 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -134,12 +134,13 @@
Error setClientTargetSlotCount(Display display) override;
- Error validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests) override;
+ Error validateDisplay(Display display, nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) override;
Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) override;
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) override;
Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
/* see setClientTarget for the purpose of slot */
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 6704d88..082717a 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -174,11 +174,13 @@
virtual Error setClientTargetSlotCount(Display display) = 0;
virtual Error validateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests) = 0;
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) = 0;
virtual Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) = 0;
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) = 0;
virtual Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) = 0;
/* see setClientTarget for the purpose of slot */
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 0c2b77d..bc763b2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -517,11 +517,12 @@
return static_cast<Error>(intError);
}
-Error Display::validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+Error Display::validate(nsecs_t expectedPresentTime, int32_t frameIntervalNs, uint32_t* outNumTypes,
uint32_t* outNumRequests) {
uint32_t numTypes = 0;
uint32_t numRequests = 0;
- auto intError = mComposer.validateDisplay(mId, expectedPresentTime, &numTypes, &numRequests);
+ auto intError = mComposer.validateDisplay(mId, expectedPresentTime, frameIntervalNs, &numTypes,
+ &numRequests);
auto error = static_cast<Error>(intError);
if (error != Error::NONE && !hasChangesError(error)) {
return error;
@@ -532,14 +533,15 @@
return error;
}
-Error Display::presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests, sp<android::Fence>* outPresentFence,
- uint32_t* state) {
+Error Display::presentOrValidate(nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
+ sp<android::Fence>* outPresentFence, uint32_t* state) {
uint32_t numTypes = 0;
uint32_t numRequests = 0;
int32_t presentFenceFd = -1;
- auto intError = mComposer.presentOrValidateDisplay(mId, expectedPresentTime, &numTypes,
- &numRequests, &presentFenceFd, state);
+ auto intError =
+ mComposer.presentOrValidateDisplay(mId, expectedPresentTime, frameIntervalNs, &numTypes,
+ &numRequests, &presentFenceFd, state);
auto error = static_cast<Error>(intError);
if (error != Error::NONE && !hasChangesError(error)) {
return error;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 23dd3e5..e7f807f 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -148,9 +148,10 @@
const android::sp<android::Fence>& releaseFence) = 0;
[[nodiscard]] virtual hal::Error setPowerMode(hal::PowerMode mode) = 0;
[[nodiscard]] virtual hal::Error setVsyncEnabled(hal::Vsync enabled) = 0;
- [[nodiscard]] virtual hal::Error validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests) = 0;
+ [[nodiscard]] virtual hal::Error validate(nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) = 0;
[[nodiscard]] virtual hal::Error presentOrValidate(nsecs_t expectedPresentTime,
+ int32_t frameIntervalNs,
uint32_t* outNumTypes,
uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence,
@@ -233,10 +234,10 @@
const android::sp<android::Fence>& releaseFence) override;
hal::Error setPowerMode(hal::PowerMode) override;
hal::Error setVsyncEnabled(hal::Vsync enabled) override;
- hal::Error validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+ hal::Error validate(nsecs_t expectedPresentTime, int32_t frameIntervalNs, uint32_t* outNumTypes,
uint32_t* outNumRequests) override;
- hal::Error presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests,
+ hal::Error presentOrValidate(nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence,
uint32_t* state) override;
ftl::Future<hal::Error> setDisplayBrightness(
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 1d9f9ce..6be57d4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -498,7 +498,7 @@
status_t HWComposer::getDeviceCompositionChanges(
HalDisplayId displayId, bool frameUsesClientComposition,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
- nsecs_t expectedPresentTime,
+ nsecs_t expectedPresentTime, Fps frameInterval,
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
ATRACE_CALL();
@@ -545,10 +545,10 @@
}
if (canSkipValidate) {
- sp<Fence> outPresentFence;
+ sp<Fence> outPresentFence = Fence::NO_FENCE;
uint32_t state = UINT32_MAX;
- error = hwcDisplay->presentOrValidate(expectedPresentTime, &numTypes, &numRequests,
- &outPresentFence, &state);
+ error = hwcDisplay->presentOrValidate(expectedPresentTime, frameInterval.getPeriodNsecs(),
+ &numTypes, &numRequests, &outPresentFence, &state);
if (!hasChangesError(error)) {
RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR);
}
@@ -563,7 +563,8 @@
}
// Present failed but Validate ran.
} else {
- error = hwcDisplay->validate(expectedPresentTime, &numTypes, &numRequests);
+ error = hwcDisplay->validate(expectedPresentTime, frameInterval.getPeriodNsecs(), &numTypes,
+ &numRequests);
}
ALOGV("SkipValidate failed, Falling back to SLOW validate/present");
if (!hasChangesError(error)) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 51e9319..5846c07 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -147,7 +147,8 @@
virtual status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
- nsecs_t expectedPresentTime, std::optional<DeviceRequestedChanges>* outChanges) = 0;
+ nsecs_t expectedPresentTime, Fps frameInterval,
+ std::optional<DeviceRequestedChanges>* outChanges) = 0;
virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
@@ -347,7 +348,7 @@
status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
- nsecs_t expectedPresentTime,
+ nsecs_t expectedPresentTime, Fps frameInterval,
std::optional<DeviceRequestedChanges>* outChanges) override;
status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index c13e568..1e7c6da 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -665,7 +665,8 @@
}
Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
- uint32_t* outNumTypes, uint32_t* outNumRequests) {
+ int32_t /*frameIntervalNs*/, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
ATRACE_NAME("HwcValidateDisplay");
mWriter.selectDisplay(display);
mWriter.validateDisplay();
@@ -681,8 +682,9 @@
}
Error HidlComposer::presentOrValidateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) {
+ int32_t /*frameIntervalNs*/, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) {
ATRACE_NAME("HwcPresentOrValidateDisplay");
mWriter.selectDisplay(display);
mWriter.presentOrvalidateDisplay();
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 1004ddd..5c19b47 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -236,12 +236,13 @@
Error setClientTargetSlotCount(Display display) override;
- Error validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests) override;
+ Error validateDisplay(Display display, nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) override;
Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) override;
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) override;
Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
/* see setClientTarget for the purpose of slot */
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
index 6d0dbc7..13b8a6c 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -110,6 +110,7 @@
/// Trunk stable server flags ///
DUMP_SERVER_FLAG(late_boot_misc2);
DUMP_SERVER_FLAG(dont_skip_on_early);
+ DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
/// Trunk stable readonly flags ///
DUMP_READ_ONLY_FLAG(connected_display);
@@ -190,6 +191,7 @@
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
+FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
/// Exceptions ///
bool FlagManager::dont_skip_on_early() const {
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
index cefce9b..e3e4f80 100644
--- a/services/surfaceflinger/FlagManager.h
+++ b/services/surfaceflinger/FlagManager.h
@@ -49,6 +49,7 @@
/// Trunk stable server flags ///
bool late_boot_misc2() const;
bool dont_skip_on_early() const;
+ bool refresh_rate_overlay_on_external_display() const;
/// Trunk stable readonly flags ///
bool connected_display() const;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 1b1307b..dfbb55d 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -1074,6 +1074,66 @@
});
}
+void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousActualPresentTime) const {
+ nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0;
+ const constexpr float kThresh = 0.5f;
+ const constexpr float kRange = 1.5f;
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ if (previousActualPresentTime != 0 &&
+ static_cast<float>(mSurfaceFlingerActuals.presentTime - previousActualPresentTime) >=
+ static_cast<float>(mRenderRate.getPeriodNsecs()) * kRange &&
+ static_cast<float>(surfaceFrame->getPredictions().presentTime) <=
+ (static_cast<float>(mSurfaceFlingerActuals.presentTime) -
+ kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
+ static_cast<float>(surfaceFrame->getPredictions().presentTime) >=
+ (static_cast<float>(previousActualPresentTime) -
+ kThresh * static_cast<float>(mRenderRate.getPeriodNsecs()))) {
+ skippedFrameStartTime = surfaceFrame->getPredictions().endTime;
+ skippedFramePresentTime = surfaceFrame->getPredictions().presentTime;
+ break;
+ }
+ }
+
+ // add slice
+ if (skippedFrameStartTime != 0 && skippedFramePresentTime != 0) {
+ int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+
+ // Actual timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(static_cast<uint64_t>(skippedFrameStartTime + monoBootOffset));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start();
+
+ actualDisplayFrameStartEvent->set_cookie(actualTimelineCookie);
+
+ actualDisplayFrameStartEvent->set_token(0);
+ actualDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
+ actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ actualDisplayFrameStartEvent->set_gpu_composition(false);
+ actualDisplayFrameStartEvent->set_prediction_type(toProto(PredictionState::Valid));
+ actualDisplayFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
+ actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(JankType::Dropped));
+ });
+
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameEndEvent = event->set_frame_end();
+
+ actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+ }
+}
+
void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid,
nsecs_t monoBootOffset) const {
int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
@@ -1115,7 +1175,8 @@
});
}
-void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const {
+void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousActualPresentTime) const {
if (mSurfaceFrames.empty()) {
// We don't want to trace display frames without any surface frames updates as this cannot
// be janky
@@ -1138,6 +1199,8 @@
for (auto& surfaceFrame : mSurfaceFrames) {
surfaceFrame->trace(mToken, monoBootOffset);
}
+
+ addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousActualPresentTime);
}
float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
@@ -1229,7 +1292,7 @@
const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
+ displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime);
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
@@ -1245,7 +1308,7 @@
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
+ displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime);
mPreviousPresentTime = signalTime;
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 538ea12..6598e21 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -378,7 +378,8 @@
// Emits a packet for perfetto tracing. The function body will be executed only if tracing
// is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME
// and SYSTEM_TIME_MONOTONIC.
- void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
+ void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousActualPresentTime) const;
// Sets the token, vsyncPeriod, predictions and SF start time.
void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate,
std::optional<TimelineItem> predictions, nsecs_t wakeUpTime);
@@ -411,6 +412,8 @@
void dump(std::string& result, nsecs_t baseTime) const;
void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
+ void addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousActualPresentTime) const;
void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
nsecs_t previousPresentTime);
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 2ac7319..6752a0b 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -268,8 +268,7 @@
}
void RefreshRateOverlay::changeRenderRate(Fps renderFps) {
- if (mFeatures.test(Features::RenderRate) && mVsyncRate &&
- FlagManager::getInstance().vrr_config()) {
+ if (mFeatures.test(Features::RenderRate) && mVsyncRate && FlagManager::getInstance().misc1()) {
mRenderFps = renderFps;
const auto buffer = getOrCreateBuffers(*mVsyncRate, renderFps)[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8fc9cba..450ba1d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -167,6 +167,27 @@
info->setDefaultLayerVote(getVoteType(frameRateCompatibility, contentDetectionEnabled));
}
+void LayerHistory::setLayerProperties(int32_t id, const LayerProps& properties) {
+ std::lock_guard lock(mLock);
+
+ auto [found, layerPair] = findLayer(id);
+ if (found == LayerStatus::NotFound) {
+ // Offscreen layer
+ ALOGV("%s: %d not registered", __func__, id);
+ return;
+ }
+
+ const auto& info = layerPair->second;
+ info->setProperties(properties);
+
+ // Activate layer if inactive and visible.
+ if (found == LayerStatus::LayerInInactiveMap && info->isVisible()) {
+ mActiveLayerInfos.insert(
+ {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
+ mInactiveLayerInfos.erase(id);
+ }
+}
+
auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
ATRACE_CALL();
Summary summary;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index bac1ec6..5a9445b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -73,7 +73,7 @@
// does not set a preference for refresh rate.
void setDefaultFrameRateCompatibility(int32_t id, FrameRateCompatibility frameRateCompatibility,
bool contentDetectionEnabled);
-
+ void setLayerProperties(int32_t id, const LayerProps&);
using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 8d18769..bf3a7bc 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -75,6 +75,10 @@
}
}
+void LayerInfo::setProperties(const android::scheduler::LayerProps& properties) {
+ *mLayerProps = properties;
+}
+
bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
mFrameTimeValidSince.time_since_epoch())
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 03ab0df..d24fc33 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -182,6 +182,8 @@
// layer can go back to whatever vote it had before the app voted for it.
void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
+ void setProperties(const LayerProps&);
+
// Resets the layer vote to its default.
void resetLayerVote() {
mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default, FrameRateCategory::Default};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 56a4ae2..f41243c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -654,6 +654,10 @@
mFeatures.test(Feature::kContentDetection));
}
+void Scheduler::setLayerProperties(int32_t id, const android::scheduler::LayerProps& properties) {
+ mLayerHistory.setLayerProperties(id, properties);
+}
+
void Scheduler::chooseRefreshRateForContent(
const surfaceflinger::frontend::LayerHierarchy* hierarchy,
bool updateAttachedChoreographer) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a02180a..c78051a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -234,6 +234,7 @@
nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
void setDefaultFrameRateCompatibility(int32_t id, scheduler::FrameRateCompatibility);
+ void setLayerProperties(int32_t id, const LayerProps&);
void deregisterLayer(Layer*);
void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 19e951a..2806450 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -83,7 +83,7 @@
};
// The frame rate category of a Layer.
-enum class FrameRateCategory {
+enum class FrameRateCategory : int32_t {
Default,
NoPreference,
Low,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 15cae33..9c8555e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2219,7 +2219,8 @@
snapshot->changes.any(Changes::Geometry));
const bool hasChanges =
- snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) ||
+ snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation |
+ Changes::Geometry | Changes::Visibility) ||
(snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) !=
0;
@@ -2250,6 +2251,10 @@
.isFrontBuffered = snapshot->isFrontBuffered(),
};
+ if (snapshot->changes.any(Changes::Geometry | Changes::Visibility)) {
+ mScheduler->setLayerProperties(snapshot->sequence, layerProps);
+ }
+
if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
snapshot->defaultFrameRateCompatibility);
@@ -2621,6 +2626,10 @@
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
+ if (display->getId() == pacesetterId) {
+ // TODO(b/255601557) Update frameInterval per display
+ refreshArgs.frameInterval = display->refreshRateSelector().getActiveMode().fps;
+ }
}
mPowerAdvisor->setDisplays(displayIds);
@@ -5987,7 +5996,9 @@
});
out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy() << "\n\n";
+ << mLayerHierarchyBuilder.getHierarchy()
+ << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n";
compositionLayers = out.str();
dumpHwcLayersMinidump(compositionLayers);
}
@@ -6267,7 +6278,9 @@
});
out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy().dump() << "\n\n";
+ << mLayerHierarchyBuilder.getHierarchy().dump()
+ << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy().dump() << "\n\n";
result.append(out.str());
})
.get();
@@ -8409,7 +8422,8 @@
void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
for (const auto& [id, display] : mPhysicalDisplays) {
- if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+ if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal ||
+ FlagManager::getInstance().refresh_rate_overlay_on_external_display()) {
if (const auto device = getDisplayDeviceLocked(id)) {
const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD(
kMainThreadContext) {
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index 910e685..243b8e0 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -74,9 +74,17 @@
],
fuzz_config: {
cc: [
- "android-media-fuzzing-reports@google.com",
+ "android-cogs-eng@google.com",
],
- componentid: 155276,
+ componentid: 1075131,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libsurfaceflinger library",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index f22315a..afb5f5c 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -163,16 +163,22 @@
void DisplayHardwareFuzzer::validateDisplay(Hwc2::AidlComposer* composer, Display display) {
uint32_t outNumTypes, outNumRequests;
- composer->validateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), &outNumTypes,
- &outNumRequests);
+ const auto frameIntervalRange =
+ mFdp.ConsumeIntegralInRange<int32_t>(Fps::fromValue(1).getPeriodNsecs(),
+ Fps::fromValue(120).getPeriodNsecs());
+ composer->validateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), frameIntervalRange,
+ &outNumTypes, &outNumRequests);
}
void DisplayHardwareFuzzer::presentOrValidateDisplay(Hwc2::AidlComposer* composer,
Display display) {
int32_t outPresentFence;
uint32_t outNumTypes, outNumRequests, state;
- composer->presentOrValidateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), &outNumTypes,
- &outNumRequests, &outPresentFence, &state);
+ const auto frameIntervalRange =
+ mFdp.ConsumeIntegralInRange<int32_t>(Fps::fromValue(1).getPeriodNsecs(),
+ Fps::fromValue(120).getPeriodNsecs());
+ composer->presentOrValidateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), frameIntervalRange,
+ &outNumTypes, &outNumRequests, &outPresentFence, &state);
}
void DisplayHardwareFuzzer::setOutputBuffer(Hwc2::AidlComposer* composer, Display display) {
@@ -223,7 +229,10 @@
mHwc.getDeviceCompositionChanges(halDisplayID,
mFdp.ConsumeBool() /*frameUsesClientComposition*/,
std::chrono::steady_clock::now(),
- mFdp.ConsumeIntegral<nsecs_t>(), &outChanges);
+ mFdp.ConsumeIntegral<nsecs_t>(),
+ Fps::fromValue(
+ mFdp.ConsumeFloatingPointInRange<float>(1.f, 120.f)),
+ &outChanges);
}
void DisplayHardwareFuzzer::getDisplayedContentSamplingAttributes(HalDisplayId halDisplayID) {
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index a81f9b8..bb3c94a 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -70,3 +70,9 @@
is_fixed_read_only: true
}
+flag {
+ name: "refresh_rate_overlay_on_external_display"
+ namespace: "core_graphics"
+ description: "enable refresh rate indicator on the external display"
+ bug: "301647974"
+}
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 9269e7c..c9af432 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -47,7 +47,6 @@
ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- mCaptureArgs.displayToken = mDisplay;
}
virtual void TearDown() {
@@ -279,8 +278,6 @@
const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
sp<SurfaceControl> mBlackBgSurface;
-
- DisplayCaptureArgs mCaptureArgs;
ScreenCaptureResults mCaptureResults;
private:
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 96cc333..79864e0 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -15,6 +15,8 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <sys/types.h>
+#include <cstdint>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -31,26 +33,22 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- ASSERT_FALSE(mDisplayToken == nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode));
- const ui::Size& resolution = mode.resolution;
-
- mDisplaySize = resolution;
+ // Root surface
+ mRootSurfaceControl =
+ createLayer(String8("RootTestSurface"), mDisplayWidth, mDisplayHeight, 0);
+ ASSERT_TRUE(mRootSurfaceControl != nullptr);
+ ASSERT_TRUE(mRootSurfaceControl->isValid());
// Background surface
- mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
- resolution.getHeight(), 0);
+ mBGSurfaceControl = createLayer(String8("BG Test Surface"), mDisplayWidth, mDisplayHeight,
+ 0, mRootSurfaceControl.get());
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
// Foreground surface
- mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+ mFGSurfaceControl =
+ createLayer(String8("FG Test Surface"), 64, 64, 0, mRootSurfaceControl.get());
ASSERT_TRUE(mFGSurfaceControl != nullptr);
ASSERT_TRUE(mFGSurfaceControl->isValid());
@@ -58,7 +56,7 @@
TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
+ t.setDisplayLayerStack(mDisplay, ui::DEFAULT_LAYER_STACK);
t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
@@ -66,25 +64,22 @@
.setPosition(mFGSurfaceControl, 64, 64)
.show(mFGSurfaceControl);
});
+
+ mCaptureArgs.sourceCrop = mDisplayRect;
+ mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
virtual void TearDown() {
LayerTransactionTest::TearDown();
mBGSurfaceControl = 0;
mFGSurfaceControl = 0;
-
- // Restore display rotation
- asTransaction([&](Transaction& t) {
- Rect displayBounds{mDisplaySize};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, displayBounds, displayBounds);
- });
}
+ sp<SurfaceControl> mRootSurfaceControl;
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
std::unique_ptr<ScreenCapture> mCapture;
- sp<IBinder> mDisplayToken;
- ui::Size mDisplaySize;
+ LayerCaptureArgs mCaptureArgs;
};
TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
@@ -92,7 +87,8 @@
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32,
ISurfaceComposerClient::eSecure |
- ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
@@ -100,14 +96,14 @@
{
// Ensure the UID is not root because root has all permissions
UIDFaker f(AID_APP_START);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
}
UIDFaker f(AID_SYSTEM);
// By default the system can capture screenshots with secure layers but they
// will be blacked out
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
{
SCOPED_TRACE("as system");
@@ -117,10 +113,8 @@
// Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
// to receive them...we are expected to take care with the results.
- DisplayCaptureArgs args;
- args.displayToken = mDisplay;
- args.captureSecureLayers = true;
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ mCaptureArgs.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
@@ -131,7 +125,8 @@
ASSERT_NO_FATAL_FAILURE(
parentLayer = createLayer("parent-test", 32, 32,
ISurfaceComposerClient::eSecure |
- ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parentLayer, Color::RED, 32, 32));
sp<SurfaceControl> childLayer;
@@ -152,10 +147,8 @@
// Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
// to receive them...we are expected to take care with the results.
- DisplayCaptureArgs args;
- args.displayToken = mDisplay;
- args.captureSecureLayers = true;
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ mCaptureArgs.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
sc.expectColor(Rect(0, 0, 10, 10), Color::BLUE);
@@ -232,7 +225,7 @@
TEST_F(ScreenCaptureTest, CaptureLayerExcludeThroughDisplayArgs) {
mCaptureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
- ScreenCapture::captureDisplay(&mCapture, mCaptureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectBGColor(0, 0);
// Doesn't capture FG layer which is at 64, 64
mCapture->expectBGColor(64, 64);
@@ -605,60 +598,55 @@
mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
}
-TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) {
- uid_t fakeUid = 12345;
+TEST_F(ScreenCaptureTest, ScreenshotProtectedBuffer) {
+ const uint32_t bufferWidth = 60;
+ const uint32_t bufferHeight = 60;
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
+ sp<SurfaceControl> layer =
+ createLayer(String8("Colored surface"), bufferWidth, bufferHeight,
+ ISurfaceComposerClient::eFXSurfaceBufferState, mRootSurfaceControl.get());
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
- ISurfaceComposerClient::eFXSurfaceBufferQueue,
- mBGSurfaceControl.get()));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
- Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+ sp<Surface> surface = layer->getSurface();
+ ASSERT_TRUE(surface != nullptr);
+ sp<ANativeWindow> anw(surface);
- // Make sure red layer with the background layer is screenshot.
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
- mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED));
- // From non system uid, can't request screenshot without a specified uid.
- UIDFaker f(fakeUid);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(captureArgs, mCaptureResults));
+ int fenceFd;
+ ANativeWindowBuffer* buf = nullptr;
- // Make screenshot request with current uid set. No layers were created with the current
- // uid so screenshot will be black.
- captureArgs.uid = fakeUid;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ // End test if device does not support USAGE_PROTECTED
+ // b/309965549 This check does not exit the test when running on AVDs
+ status_t err = anw->dequeueBuffer(anw.get(), &buf, &fenceFd);
+ if (err) {
+ return;
+ }
+ anw->queueBuffer(anw.get(), buf, fenceFd);
- sp<SurfaceControl> layerWithFakeUid;
- // Create a new layer with the current uid
- ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
- createLayer("new test layer", 32, 32,
- ISurfaceComposerClient::eFXSurfaceBufferQueue,
- mBGSurfaceControl.get()));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
- Transaction()
- .show(layerWithFakeUid)
- .setLayer(layerWithFakeUid, INT32_MAX)
- .setPosition(layerWithFakeUid, 128, 128)
- .apply();
+ // USAGE_PROTECTED buffer is read as a black screen
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
- // Screenshot from the fakeUid caller with the uid requested allows the layer
- // with that uid to be screenshotted. Everything else is black
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
- mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
+ ScreenCapture sc(captureResults.buffer, captureResults.capturedHdrLayers);
+ sc.expectColor(Rect(0, 0, bufferWidth, bufferHeight), Color::BLACK);
+
+ // Reading color data will expectedly result in crash, only check usage bit
+ // b/309965549 Checking that the usage bit is protected does not work for
+ // devices that do not support usage protected.
+ mCaptureArgs.allowProtected = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
+ // ASSERT_EQ(GRALLOC_USAGE_PROTECTED, GRALLOC_USAGE_PROTECTED &
+ // captureResults.buffer->getUsage());
}
-TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) {
+TEST_F(ScreenCaptureTest, CaptureLayer) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ mRootSurfaceControl.get()));
const Color layerColor = Color::RED;
const Rect bounds = Rect(10, 10, 40, 40);
@@ -666,17 +654,13 @@
Transaction()
.show(layer)
.hide(mFGSurfaceControl)
- .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setCrop(layer, bounds)
.apply();
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
-
{
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(bounds, layerColor);
mCapture->expectBorder(bounds, {63, 63, 195, 255});
}
@@ -689,17 +673,18 @@
{
// Can't screenshot test layer since it now has flag
// eLayerSkipScreenshot
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(bounds, {63, 63, 195, 255});
mCapture->expectBorder(bounds, {63, 63, 195, 255});
}
}
-TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) {
+TEST_F(ScreenCaptureTest, CaptureLayerChild) {
sp<SurfaceControl> layer;
sp<SurfaceControl> childLayer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("test layer", 0, 0,
ISurfaceComposerClient::eFXSurfaceEffect,
layer.get()));
@@ -713,7 +698,6 @@
.show(layer)
.show(childLayer)
.hide(mFGSurfaceControl)
- .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
@@ -721,11 +705,8 @@
.setCrop(childLayer, childBounds)
.apply();
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
-
{
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(childBounds, childColor);
mCapture->expectBorder(childBounds, layerColor);
mCapture->expectBorder(bounds, {63, 63, 195, 255});
@@ -739,7 +720,7 @@
{
// Can't screenshot child layer since the parent has the flag
// eLayerSkipScreenshot
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(childBounds, {63, 63, 195, 255});
mCapture->expectBorder(childBounds, {63, 63, 195, 255});
mCapture->expectBorder(bounds, {63, 63, 195, 255});
@@ -860,14 +841,10 @@
Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply();
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplay;
-
{
// Validate that the red layer is not on screen
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
- mCapture->expectColor(Rect(0, 0, mDisplaySize.width, mDisplaySize.height),
- {63, 63, 195, 255});
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
+ mCapture->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), {63, 63, 195, 255});
}
LayerCaptureArgs captureArgs;
@@ -878,42 +855,6 @@
mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_F(ScreenCaptureTest, CaptureDisplayWith90DegRotation) {
- asTransaction([&](Transaction& t) {
- Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_90, newDisplayBounds, newDisplayBounds);
- });
-
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplayToken;
- displayCaptureArgs.width = mDisplaySize.width;
- displayCaptureArgs.height = mDisplaySize.height;
- displayCaptureArgs.useIdentityTransform = true;
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-
- mCapture->expectBGColor(0, 0);
- mCapture->expectFGColor(mDisplaySize.width - 65, 65);
-}
-
-TEST_F(ScreenCaptureTest, CaptureDisplayWith270DegRotation) {
- asTransaction([&](Transaction& t) {
- Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_270, newDisplayBounds, newDisplayBounds);
- });
-
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplayToken;
- displayCaptureArgs.width = mDisplaySize.width;
- displayCaptureArgs.height = mDisplaySize.height;
- displayCaptureArgs.useIdentityTransform = true;
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-
- std::this_thread::sleep_for(std::chrono::seconds{5});
-
- mCapture->expectBGColor(mDisplayWidth - 1, mDisplaySize.height - 1);
- mCapture->expectFGColor(65, mDisplaySize.height - 65);
-}
-
TEST_F(ScreenCaptureTest, CaptureNonHdrLayer) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index d0ab105..c5d118c 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -80,29 +80,22 @@
sp<SurfaceControl> mParent;
sp<SurfaceControl> mLayer;
std::unique_ptr<ScreenCapture> mCapture;
+ gui::LayerCaptureArgs captureArgs;
};
TEST_F(TextureFilteringTest, NoFiltering) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
- captureArgs.sourceCrop = Rect{100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
}
TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
- Transaction().setBufferCrop(mLayer, Rect{0, 0, 100, 100}).apply();
-
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
@@ -112,24 +105,20 @@
TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
// Expect filtering because the output source crop is stretched to the output buffer's size.
TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
+ captureArgs.frameScaleX = 2;
+ captureArgs.frameScaleY = 2;
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
@@ -138,20 +127,17 @@
// buffer's size.
TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
-
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
+ captureArgs.frameScaleX = 2;
+ captureArgs.frameScaleY = 2;
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
// Expect filtering because the layer is scaled up.
TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.frameScaleX = 2;
captureArgs.frameScaleY = 2;
@@ -162,7 +148,6 @@
// Expect no filtering because the output buffer's size matches the source crop.
TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -176,7 +161,6 @@
TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) {
Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -187,12 +171,9 @@
// Expect no filtering because the output source crop and output buffer are the same size.
TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 50;
- captureArgs.height = 50;
+ captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
@@ -202,9 +183,8 @@
TEST_F(TextureFilteringTest, LayerCropDisplayFrameMatchNoFiltering) {
Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
@@ -214,9 +194,8 @@
TEST_F(TextureFilteringTest, ParentCropNoFiltering) {
Transaction().setCrop(mParent, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
@@ -226,7 +205,6 @@
TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) {
Transaction().setPosition(mParent, 100, 100).apply();
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mParent->getHandle();
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
ScreenCapture::captureLayers(&mCapture, captureArgs);
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 8e13c0d..ee967979 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -347,7 +347,7 @@
}
static void setupHwcCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+ EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _))
.Times(1);
EXPECT_CALL(*test->mDisplaySurface,
@@ -356,12 +356,12 @@
}
static void setupHwcClientCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+ EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _))
.Times(1);
}
static void setupHwcForcedClientCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _, _)).Times(1);
+ EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
}
static void setupRECompositionCallExpectations(CompositionTest* test) {
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 636d852..e040028 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "gmock/gmock-spec-builders.h"
#include "mock/MockTimeStats.h"
#undef LOG_TAG
@@ -1059,6 +1058,94 @@
EXPECT_EQ(received.cookie(), source.cookie());
}
+TEST_F(FrameTimelineTest, traceDisplayFrameSkipped) {
+ // setup 2 display frames
+ // DF 1: [22,40] -> [5, 40]
+ // DF : [36, 70] (Skipped one, added by the trace)
+ // DF 2: [82, 100] -> SF [25, 70]
+ auto tracingSession = getTracingSessionForTest();
+ tracingSession->StartBlocking();
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({82, 90, 100});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ int64_t traceCookie = snoopCurrentTraceCookie();
+
+ // set up 1st display frame
+ FrameTimelineInfo ftInfo1;
+ ftInfo1.vsyncId = surfaceFrameToken1;
+ ftInfo1.inputEventId = sInputEventId;
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo1, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+ surfaceFrame1->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_30);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(30, presentFence1);
+ presentFence1->signalForTest(40);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ FrameTimelineInfo ftInfo2;
+ ftInfo2.vsyncId = surfaceFrameToken2;
+ ftInfo2.inputEventId = sInputEventId;
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+
+ // set up 2nd display frame
+ surfaceFrame2->setAcquireFenceTime(36);
+ mFrameTimeline->setSfWakeUp(sfToken2, 82, RR_11, RR_30);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(90, presentFence2);
+ presentFence2->signalForTest(100);
+
+ // the token of skipped Display Frame
+ auto protoSkippedActualDisplayFrameStart =
+ createProtoActualDisplayFrameStart(traceCookie + 9, 0, kSurfaceFlingerPid,
+ FrameTimelineEvent::PRESENT_DROPPED, true, false,
+ FrameTimelineEvent::JANK_DROPPED,
+ FrameTimelineEvent::PREDICTION_VALID);
+ auto protoSkippedActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 9);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // 8 Valid Display Frames + 8 Valid Surface Frames + 2 Skipped Display Frames
+ EXPECT_EQ(packets.size(), 18u);
+
+ // Packet - 16: Actual skipped Display Frame Start
+ // the timestamp should be equal to the 2nd expected surface frame's end time
+ const auto& packet16 = packets[16];
+ ASSERT_TRUE(packet16.has_timestamp());
+ EXPECT_EQ(packet16.timestamp(), 36u);
+ ASSERT_TRUE(packet16.has_frame_timeline_event());
+
+ const auto& event16 = packet16.frame_timeline_event();
+ const auto& actualSkippedDisplayFrameStart = event16.actual_display_frame_start();
+ validateTraceEvent(actualSkippedDisplayFrameStart, protoSkippedActualDisplayFrameStart);
+
+ // Packet - 17: Actual skipped Display Frame End
+ // the timestamp should be equal to the 2nd expected surface frame's present time
+ const auto& packet17 = packets[17];
+ ASSERT_TRUE(packet17.has_timestamp());
+ EXPECT_EQ(packet17.timestamp(), 70u);
+ ASSERT_TRUE(packet17.has_frame_timeline_event());
+
+ const auto& event17 = packet17.frame_timeline_event();
+ const auto& actualSkippedDisplayFrameEnd = event17.frame_end();
+ validateTraceEvent(actualSkippedDisplayFrameEnd, protoSkippedActualDisplayFrameEnd);
+}
+
TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 190c0e8..befef48 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -839,6 +839,81 @@
ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
}
+TEST_F(LayerHistoryIntegrationTest, hidingLayerUpdatesLayerHistory) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+
+ hideLayer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summary.empty());
+ EXPECT_EQ(0u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, showingLayerUpdatesLayerHistory) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ hideLayer(1);
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ auto summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summary.empty());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ showLayer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, updatingGeometryUpdatesWeight) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1,
+ std::make_shared<
+ renderengine::mock::FakeExternalTexture>(100U /*width*/, 100U /*height*/, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_PROTECTED /*usage*/));
+ mFlinger.setLayerHistoryDisplayArea(100 * 100);
+ updateLayerSnapshotsAndLayerHistory(time);
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+
+ auto startingWeight = summary[0].weight;
+
+ setMatrix(1, 0.1f, 0.f, 0.f, 0.1f);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_GT(startingWeight, summary[0].weight);
+}
+
class LayerHistoryIntegrationTestParameterized
: public LayerHistoryIntegrationTest,
public testing::WithParamInterface<std::chrono::nanoseconds> {};
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 57babaf..2cc6dc7 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -789,6 +789,143 @@
EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12 (frame rate set to default with strategy default)
+ // │ │ ├── 121
+ // │ │ └── 122 (frame rate set to 123.f)
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+ setFrameRate(12, -1.f, 0, 0);
+ setFrameRateSelectionStrategy(12, 0 /* Default */);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent 1 gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+ EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_FALSE(getSnapshot({.id = 121})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 123.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 123.f);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+}
+
+TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) {
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12 (frame rate category set to high with strategy OverrideChildren)
+ // │ │ ├── 121
+ // │ │ └── 122 (frame rate set to 123.f but should be overridden by layer 12)
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+ setFrameRateCategory(12, 4 /* high */);
+ setFrameRate(122, 123.f, 0, 0);
+ setFrameRateSelectionStrategy(12, 1 /* OverrideChildren */);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent 1 gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.category, FrameRateCategory::High);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.category, FrameRateCategory::High);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::High);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::High);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12 (frame rate category to default with strategy default)
+ // │ │ ├── 121
+ // │ │ └── 122 (frame rate set to 123.f)
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+ setFrameRateCategory(12, 0 /* default */);
+ setFrameRateSelectionStrategy(12, 0 /* Default */);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent 1 gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+ EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 123.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 123.f);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
}
TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index d0290ea..b80cb66 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -103,7 +103,7 @@
EXPECT_CALL(*mDisplaySurface,
prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
.Times(1);
- EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)).WillOnce([] {
+ EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
constexpr Duration kMockHwcRunTime = 20ms;
std::this_thread::sleep_for(kMockHwcRunTime);
return hardware::graphics::composer::V2_1::Error::NONE;
@@ -124,7 +124,7 @@
EXPECT_CALL(*mDisplaySurface,
prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
.Times(1);
- EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)).WillOnce([] {
+ EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
constexpr Duration kMockHwcRunTime = 20ms;
std::this_thread::sleep_for(kMockHwcRunTime);
return hardware::graphics::composer::V2_1::Error::NONE;
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index d0b2199..bca14f5 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -623,6 +623,9 @@
void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
+ auto setLayerHistoryDisplayArea(uint32_t displayArea) {
+ return mFlinger->mScheduler->onActiveDisplayAreaChanged(displayArea);
+ };
auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
auto setDaltonizerType(ColorBlindnessType type) {
mFlinger->mDaltonizer.setType(type);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 0b07745..3b74f0a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -95,9 +95,9 @@
MOCK_METHOD2(setPowerMode, Error(Display, IComposerClient::PowerMode));
MOCK_METHOD2(setVsyncEnabled, Error(Display, IComposerClient::Vsync));
MOCK_METHOD1(setClientTargetSlotCount, Error(Display));
- MOCK_METHOD4(validateDisplay, Error(Display, nsecs_t, uint32_t*, uint32_t*));
- MOCK_METHOD6(presentOrValidateDisplay,
- Error(Display, nsecs_t, uint32_t*, uint32_t*, int*, uint32_t*));
+ MOCK_METHOD(Error, validateDisplay, (Display, nsecs_t, int32_t, uint32_t*, uint32_t*));
+ MOCK_METHOD(Error, presentOrValidateDisplay,
+ (Display, nsecs_t, int32_t, uint32_t*, uint32_t*, int*, uint32_t*));
MOCK_METHOD4(setCursorPosition, Error(Display, Layer, int32_t, int32_t));
MOCK_METHOD5(setLayerBuffer, Error(Display, Layer, uint32_t, const sp<GraphicBuffer>&, int));
MOCK_METHOD4(setLayerBufferSlotsToClear,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 40f59b8..a7ddb6d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -76,9 +76,9 @@
(override));
MOCK_METHOD(hal::Error, setPowerMode, (hal::PowerMode), (override));
MOCK_METHOD(hal::Error, setVsyncEnabled, (hal::Vsync), (override));
- MOCK_METHOD(hal::Error, validate, (nsecs_t, uint32_t *, uint32_t *), (override));
+ MOCK_METHOD(hal::Error, validate, (nsecs_t, int32_t, uint32_t*, uint32_t*), (override));
MOCK_METHOD(hal::Error, presentOrValidate,
- (nsecs_t, uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *),
+ (nsecs_t, int32_t, uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*),
(override));
MOCK_METHOD(ftl::Future<hal::Error>, setDisplayBrightness,
(float, float, const Hwc2::Composer::DisplayBrightnessOptions &), (override));