diff options
274 files changed, 9899 insertions, 2183 deletions
diff --git a/Android.bp b/Android.bp index 7f1ef671bc..8c4dfbbea4 100644 --- a/Android.bp +++ b/Android.bp @@ -97,6 +97,12 @@ filegroup { ], } +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 493ecb414c..248e9738df 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 0000000000..1edacec384 --- /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 0000000000..c88bfe97ca --- /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 0000000000..35feea1930 --- /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 0000000000..6f5deb96b2 --- /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/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index d7c9b40d92..e14af77194 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -471,6 +471,49 @@ status_t InstalldNativeService::dump(int fd, const Vector<String16>& /* args */) return NO_ERROR; } +constexpr const char kXattrRestoreconInProgress[] = "user.restorecon_in_progress"; + +static std::string lgetfilecon(const std::string& path) { + char* context; + if (::lgetfilecon(path.c_str(), &context) < 0) { + PLOG(ERROR) << "Failed to lgetfilecon for " << path; + return {}; + } + std::string result{context}; + free(context); + return result; +} + +static bool getRestoreconInProgress(const std::string& path) { + bool inProgress = false; + if (getxattr(path.c_str(), kXattrRestoreconInProgress, &inProgress, sizeof(inProgress)) != + sizeof(inProgress)) { + if (errno != ENODATA) { + PLOG(ERROR) << "Failed to check in-progress restorecon for " << path; + } + return false; + } + return inProgress; +} + +struct RestoreconInProgress { + explicit RestoreconInProgress(const std::string& path) : mPath(path) { + bool inProgress = true; + if (setxattr(mPath.c_str(), kXattrRestoreconInProgress, &inProgress, sizeof(inProgress), + 0) != 0) { + PLOG(ERROR) << "Failed to set in-progress restorecon for " << path; + } + } + ~RestoreconInProgress() { + if (removexattr(mPath.c_str(), kXattrRestoreconInProgress) < 0) { + PLOG(ERROR) << "Failed to clear in-progress restorecon for " << mPath; + } + } + +private: + const std::string& mPath; +}; + /** * Perform restorecon of the given path, but only perform recursive restorecon * if the label of that top-level file actually changed. This can save us @@ -479,56 +522,56 @@ status_t InstalldNativeService::dump(int fd, const Vector<String16>& /* args */) static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid, bool existing) { ScopedTrace tracer("restorecon-lazy"); - int res = 0; - char* before = nullptr; - char* after = nullptr; if (!existing) { ScopedTrace tracer("new-path"); if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, SELINUX_ANDROID_RESTORECON_RECURSE) < 0) { PLOG(ERROR) << "Failed recursive restorecon for " << path; - goto fail; + return -1; } - return res; + return 0; } - // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by - // libselinux. Not needed here. - if (lgetfilecon(path.c_str(), &before) < 0) { - PLOG(ERROR) << "Failed before getfilecon for " << path; - goto fail; - } - if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) { - PLOG(ERROR) << "Failed top-level restorecon for " << path; - goto fail; - } - if (lgetfilecon(path.c_str(), &after) < 0) { - PLOG(ERROR) << "Failed after getfilecon for " << path; - goto fail; + // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here. + + // Check to see if there was an interrupted operation. + bool inProgress = getRestoreconInProgress(path); + std::string before, after; + if (!inProgress) { + if (before = lgetfilecon(path); before.empty()) { + PLOG(ERROR) << "Failed before getfilecon for " << path; + return -1; + } + if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) { + PLOG(ERROR) << "Failed top-level restorecon for " << path; + return -1; + } + if (after = lgetfilecon(path); after.empty()) { + PLOG(ERROR) << "Failed after getfilecon for " << path; + return -1; + } } // If the initial top-level restorecon above changed the label, then go // back and restorecon everything recursively - if (strcmp(before, after)) { + if (inProgress || before != after) { ScopedTrace tracer("label-change"); if (existing) { LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " << path << "; running recursive restorecon"; } + + // Temporary mark the folder as "in-progress" to resume in case of reboot/other failure. + RestoreconInProgress fence(path); + if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, SELINUX_ANDROID_RESTORECON_RECURSE) < 0) { PLOG(ERROR) << "Failed recursive restorecon for " << path; - goto fail; + return -1; } } - goto done; -fail: - res = -1; -done: - free(before); - free(after); - return res; + return 0; } static bool internal_storage_has_project_id() { // The following path is populated in setFirstBoot, so if this file is present @@ -3283,7 +3326,7 @@ binder::Status InstalldNativeService::linkNativeLibraryDirectory( } char *con = nullptr; - if (lgetfilecon(pkgdir, &con) < 0) { + if (::lgetfilecon(pkgdir, &con) < 0) { return error("Failed to lgetfilecon " + _pkgdir); } diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index c40caf56d8..c86adef118 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -353,7 +353,7 @@ static int otapreopt_chroot(const int argc, char **arg) { // Now go on and read dexopt lines from stdin and pass them on to otapreopt. int count = 1; - for (std::array<char, 1000> linebuf; + for (std::array<char, 10000> linebuf; std::cin.clear(), std::cin.getline(&linebuf[0], linebuf.size()); ++count) { // Subtract one from gcount() since getline() counts the newline. std::string line(&linebuf[0], std::cin.gcount() - 1); diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 550b37482b..89736eceb7 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -371,6 +371,12 @@ prebuilt_etc { } 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 { } 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 0000000000..4eeed2af4f --- /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.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml index bd15eb6eb2..62bb10161a 100644 --- a/data/etc/android.software.opengles.deqp.level-latest.xml +++ b/data/etc/android.software.opengles.deqp.level-latest.xml @@ -17,5 +17,5 @@ <!-- This is the standard feature indicating that the device passes OpenGL ES dEQP tests associated with the most recent level for this Android version. --> <permissions> - <feature name="android.software.opengles.deqp.level" version="132580097" /> + <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 0000000000..8b2b4c8199 --- /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/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml index 87be0709d6..0fc12b3b5f 100644 --- a/data/etc/android.software.vulkan.deqp.level-latest.xml +++ b/data/etc/android.software.vulkan.deqp.level-latest.xml @@ -17,5 +17,5 @@ <!-- This is the standard feature indicating that the device passes Vulkan dEQP tests associated with the most recent level for this Android version. --> <permissions> - <feature name="android.software.vulkan.deqp.level" version="132580097" /> + <feature name="android.software.vulkan.deqp.level" version="132645633" /> </permissions> diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index ba8b02d597..9d2c79139f 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -60,6 +60,27 @@ __BEGIN_DECLS struct APerformanceHintManager; struct APerformanceHintSession; +struct AWorkDuration; + +/** + * {@link AWorkDuration} is an opaque type that represents the breakdown of the + * actual workload duration in each component internally. + * + * A new {@link AWorkDuration} can be obtained using + * {@link AWorkDuration_create()}, when the client finishes using + * {@link AWorkDuration}, {@link AWorkDuration_release()} must be + * called to destroy and free up the resources associated with + * {@link AWorkDuration}. + * + * This file provides a set of functions to allow clients to set the measured + * work duration of each component on {@link AWorkDuration}. + * + * - AWorkDuration_setWorkPeriodStartTimestampNanos() + * - AWorkDuration_setActualTotalDurationNanos() + * - AWorkDuration_setActualCpuDurationNanos() + * - AWorkDuration_setActualGpuDurationNanos() + */ +typedef struct AWorkDuration AWorkDuration; /** * An opaque type representing a handle to a performance hint manager. @@ -102,7 +123,7 @@ typedef struct APerformanceHintSession APerformanceHintSession; * * @return APerformanceHintManager instance on success, nullptr on failure. */ -APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); +APerformanceHintManager* _Nullable APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); /** * Creates a session for the given set of threads and sets their initial target work @@ -116,9 +137,9 @@ APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID * This must be positive if using the work duration API, or 0 otherwise. * @return APerformanceHintManager instance on success, nullptr on failure. */ -APerformanceHintSession* APerformanceHint_createSession( - APerformanceHintManager* manager, - const int32_t* threadIds, size_t size, +APerformanceHintSession* _Nullable APerformanceHint_createSession( + APerformanceHintManager* _Nonnull manager, + const int32_t* _Nonnull threadIds, size_t size, int64_t initialTargetWorkDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** @@ -128,7 +149,7 @@ APerformanceHintSession* APerformanceHint_createSession( * @return the preferred update rate supported by device software. */ int64_t APerformanceHint_getPreferredUpdateRateNanos( - APerformanceHintManager* manager) __INTRODUCED_IN(__ANDROID_API_T__); + APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(__ANDROID_API_T__); /** * Updates this session's target duration for each cycle of work. @@ -140,7 +161,7 @@ int64_t APerformanceHint_getPreferredUpdateRateNanos( * EPIPE if communication with the system service has failed. */ int APerformanceHint_updateTargetWorkDuration( - APerformanceHintSession* session, + APerformanceHintSession* _Nonnull session, int64_t targetDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** @@ -157,7 +178,7 @@ int APerformanceHint_updateTargetWorkDuration( * EPIPE if communication with the system service has failed. */ int APerformanceHint_reportActualWorkDuration( - APerformanceHintSession* session, + APerformanceHintSession* _Nonnull session, int64_t actualDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** @@ -167,7 +188,7 @@ int APerformanceHint_reportActualWorkDuration( * @param session The performance hint session instance to release. */ void APerformanceHint_closeSession( - APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__); + APerformanceHintSession* _Nonnull session) __INTRODUCED_IN(__ANDROID_API_T__); /** * Set a list of threads to the performance hint session. This operation will replace @@ -184,8 +205,8 @@ void APerformanceHint_closeSession( * EPERM if any thread id doesn't belong to the application. */ int APerformanceHint_setThreads( - APerformanceHintSession* session, - const pid_t* threadIds, + APerformanceHintSession* _Nonnull session, + const pid_t* _Nonnull threadIds, size_t size) __INTRODUCED_IN(__ANDROID_API_U__); /** @@ -198,11 +219,92 @@ int APerformanceHint_setThreads( * EPIPE if communication with the system service has failed. */ int APerformanceHint_setPreferPowerEfficiency( - APerformanceHintSession* session, + APerformanceHintSession* _Nonnull session, bool enabled) __INTRODUCED_IN(__ANDROID_API_V__); +/** + * Reports the durations for the last cycle of work. + * + * The system will attempt to adjust the scheduling and performance of the + * threads within the thread group to bring the actual duration close to the target duration. + * + * @param session The {@link APerformanceHintSession} instance to update. + * @param workDuration The {@link AWorkDuration} structure of times the thread group took to + * complete its last task in nanoseconds breaking down into different components. + * + * The work period start timestamp, actual total duration and actual CPU duration must be + * positive. + * + * The actual GPU duration must be non-negative. If the actual GPU duration is 0, it means + * the actual GPU duration is not measured. + * + * @return 0 on success. + * EINVAL if session is nullptr or any duration is an invalid number. + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_reportActualWorkDuration2( + APerformanceHintSession* _Nonnull session, + AWorkDuration* _Nonnull workDuration) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Creates a new AWorkDuration. When the client finishes using {@link AWorkDuration}, it should + * call {@link AWorkDuration_release()} to destroy {@link AWorkDuration} and release all resources + * associated with it. + * + * @return AWorkDuration on success and nullptr otherwise. + */ +AWorkDuration* _Nonnull AWorkDuration_create() __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Destroys {@link AWorkDuration} and free all resources associated to it. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + */ +void AWorkDuration_release(AWorkDuration* _Nonnull WorkDuration) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the work period start timestamp in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param workPeriodStartTimestampNanos The work period start timestamp in nanoseconds based on + * CLOCK_MONOTONIC about when the work starts, the timestamp must be positive. + */ +void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t workPeriodStartTimestampNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual total work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param actualTotalDurationNanos The actual total work duration in nanoseconds, the number must be + * positive. + */ +void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualTotalDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual CPU work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds, the number must be + * positive. + */ +void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualCpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual GPU work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}. + * @param actualGpuDurationNanos The actual GPU work duration in nanoseconds, the number must be + * non-negative. If the actual GPU duration is 0, it means the actual GPU duration is + * measured. + */ +void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualGpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + __END_DECLS #endif // ANDROID_NATIVE_PERFORMANCE_HINT_H -/** @} */
\ No newline at end of file +/** @} */ diff --git a/include/android/thermal.h b/include/android/thermal.h index 1f477f8233..0b57e9376d 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -111,7 +111,7 @@ typedef struct AThermalManager AThermalManager; * It's passed the updated thermal status as parameter, as well as the * pointer provided by the client that registered a callback. */ -typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status); +typedef void (*AThermal_StatusCallback)(void* data, AThermalStatus status); /** * Acquire an instance of the thermal manager. This must be freed using @@ -222,6 +222,70 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, float AThermal_getThermalHeadroom(AThermalManager *manager, int forecastSeconds) __INTRODUCED_IN(31); +/** + * This struct defines an instance of headroom threshold value and its status. + * <p> + * The value should be monotonically non-decreasing as the thermal status increases. + * For {@link ATHERMAL_STATUS_SEVERE}, its headroom threshold is guaranteed to + * be 1.0f. For status below severe status, the value should be lower or equal + * to 1.0f, and for status above severe, the value should be larger or equal to 1.0f. + * <p> + * Also see {@link AThermal_getThermalHeadroom} for explanation on headroom, and + * {@link AThermal_getThermalHeadroomThresholds} for how to use this. + */ +struct AThermalHeadroomThreshold { + float headroom; + AThermalStatus thermalStatus; +}; + +/** + * Gets the thermal headroom thresholds for all available thermal status. + * + * A thermal status will only exist in output if the device manufacturer has the + * corresponding threshold defined for at least one of its slow-moving skin temperature + * sensors. If it's set, one should also expect to get it from + * {@link #AThermal_getCurrentThermalStatus} or {@link AThermal_StatusCallback}. + * <p> + * The headroom threshold is used to interpret the possible thermal throttling status based on + * the headroom prediction. For example, if the headroom threshold for + * {@link ATHERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75 + * (or {@code AThermal_getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system + * could be in lightly throttled state if the workload remains the same. The app can consider + * taking actions according to the nearest throttling status the difference between the headroom and + * the threshold. + * <p> + * For new devices it's guaranteed to have a single sensor, but for older devices with multiple + * sensors reporting different threshold values, the minimum threshold is taken to be conservative + * on predictions. Thus, when reading real-time headroom, it's not guaranteed that a real-time value + * of 0.75 (or {@code AThermal_getThermalHeadroom(0)}=0.75) exceeding the threshold of 0.7 above + * will always come with lightly throttled state + * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT}) but it can be lower + * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE}). + * While it's always guaranteed that the device won't be throttled heavier than the unmet + * threshold's state, so a real-time headroom of 0.75 will never come with + * {@link #ATHERMAL_STATUS_MODERATE} but always lower, and 0.65 will never come with + * {@link ATHERMAL_STATUS_LIGHT} but {@link #ATHERMAL_STATUS_NONE}. + * <p> + * The returned list of thresholds is cached on first successful query and owned by the thermal + * manager, which will not change between calls to this function. The caller should only need to + * free the manager with {@link AThermal_releaseManager}. + * + * @param manager The manager instance to use. + * Acquired via {@link AThermal_acquireManager}. + * @param outThresholds non-null output pointer to null AThermalHeadroomThreshold pointer, which + * will be set to the cached array of thresholds if thermal thresholds are supported + * by the system or device, otherwise nullptr or unmodified. + * @param size non-null output pointer whose value will be set to the size of the threshold array + * or 0 if it's not supported. + * @return 0 on success + * EINVAL if outThresholds or size_t is nullptr, or *outThresholds is not nullptr. + * EPIPE if communication with the system service has failed. + * ENOSYS if the feature is disabled by the current system. + */ +int AThermal_getThermalHeadroomThresholds(AThermalManager* manager, + const AThermalHeadroomThreshold ** outThresholds, + size_t* size) __INTRODUCED_IN(35); + #ifdef __cplusplus } #endif diff --git a/include/input/Input.h b/include/input/Input.h index bd544b5264..1c4ea6b416 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -1138,6 +1138,24 @@ private: 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 9c0c10e603..2d23b97386 100644 --- a/include/input/InputEventBuilders.h +++ b/include/input/InputEventBuilders.h @@ -160,4 +160,90 @@ private: 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/include/private/thermal_private.h b/include/private/thermal_private.h new file mode 100644 index 0000000000..951d953267 --- /dev/null +++ b/include/private/thermal_private.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#ifndef ANDROID_PRIVATE_NATIVE_THERMAL_H +#define ANDROID_PRIVATE_NATIVE_THERMAL_H + +#include <stdint.h> + +__BEGIN_DECLS + +/** + * For testing only. + */ +void AThermal_setIThermalServiceForTesting(void* iThermalService); + +__END_DECLS + +#endif // ANDROID_PRIVATE_NATIVE_THERMAL_H
\ No newline at end of file diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 620c23c1bb..f90c618a94 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -251,13 +251,15 @@ cc_library_shared { srcs: [ // Trusty-specific files "OS_android.cpp", - "trusty/logging.cpp", "trusty/OS.cpp", "trusty/RpcServerTrusty.cpp", "trusty/RpcTransportTipcTrusty.cpp", "trusty/TrustyStatus.cpp", "trusty/socket.cpp", ], + shared_libs: [ + "liblog", + ], } cc_defaults { diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index a01f447c9c..301dbf6cdf 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -19,7 +19,6 @@ #include <atomic> #include <set> -#include <android-base/logging.h> #include <android-base/unique_fd.h> #include <binder/BpBinder.h> #include <binder/IInterface.h> @@ -30,7 +29,6 @@ #include <binder/RecordedTransaction.h> #include <binder/RpcServer.h> #include <pthread.h> -#include <utils/misc.h> #include <inttypes.h> #include <stdio.h> @@ -309,7 +307,7 @@ status_t BBinder::startRecordingTransactions(const Parcel& data) { Extras* e = getOrCreateExtras(); RpcMutexUniqueLock lock(e->mLock); if (mRecordingOn) { - LOG(INFO) << "Could not start Binder recording. Another is already in progress."; + ALOGI("Could not start Binder recording. Another is already in progress."); return INVALID_OPERATION; } else { status_t readStatus = data.readUniqueFileDescriptor(&(e->mRecordingFd)); @@ -317,7 +315,7 @@ status_t BBinder::startRecordingTransactions(const Parcel& data) { return readStatus; } mRecordingOn = true; - LOG(INFO) << "Started Binder recording."; + ALOGI("Started Binder recording."); return NO_ERROR; } } @@ -341,10 +339,10 @@ status_t BBinder::stopRecordingTransactions() { if (mRecordingOn) { e->mRecordingFd.reset(); mRecordingOn = false; - LOG(INFO) << "Stopped Binder recording."; + ALOGI("Stopped Binder recording."); return NO_ERROR; } else { - LOG(INFO) << "Could not stop Binder recording. One is not in progress."; + ALOGI("Could not stop Binder recording. One is not in progress."); return INVALID_OPERATION; } } @@ -378,11 +376,11 @@ status_t BBinder::transact( err = stopRecordingTransactions(); break; case EXTENSION_TRANSACTION: - CHECK(reply != nullptr); + LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr"); err = reply->writeStrongBinder(getExtension()); break; case DEBUG_PID_TRANSACTION: - CHECK(reply != nullptr); + LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr"); err = reply->writeInt32(getDebugPid()); break; case SET_RPC_CLIENT_TRANSACTION: { @@ -415,10 +413,10 @@ status_t BBinder::transact( reply ? *reply : emptyReply, err); if (transaction) { if (status_t err = transaction->dumpToFile(e->mRecordingFd); err != NO_ERROR) { - LOG(INFO) << "Failed to dump RecordedTransaction to file with error " << err; + ALOGI("Failed to dump RecordedTransaction to file with error %d", err); } } else { - LOG(INFO) << "Failed to create RecordedTransaction object."; + ALOGI("Failed to create RecordedTransaction object."); } } } @@ -706,7 +704,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, return status; } rpcServer->setMaxThreads(binderThreadPoolMaxCount); - LOG(INFO) << "RpcBinder: Started Binder debug on " << getInterfaceDescriptor(); + ALOGI("RpcBinder: Started Binder debug on %s", String8(getInterfaceDescriptor()).c_str()); rpcServer->start(); e->mRpcServerLinks.emplace(link); LOG_RPC_DETAIL("%s(fd=%d) successful", __PRETTY_FUNCTION__, socketFdForPrint); @@ -724,20 +722,20 @@ BBinder::~BBinder() { if (!wasParceled()) { if (getExtension()) { - ALOGW("Binder %p destroyed with extension attached before being parceled.", this); + ALOGW("Binder %p destroyed with extension attached before being parceled.", this); } if (isRequestingSid()) { - ALOGW("Binder %p destroyed when requesting SID before being parceled.", this); + ALOGW("Binder %p destroyed when requesting SID before being parceled.", this); } if (isInheritRt()) { - ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this); + ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this); } #ifdef __linux__ if (getMinSchedulerPolicy() != SCHED_NORMAL) { - ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); + ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); } if (getMinSchedulerPriority() != 0) { - ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); + ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); } #endif // __linux__ } @@ -753,7 +751,7 @@ status_t BBinder::onTransact( { switch (code) { case INTERFACE_TRANSACTION: - CHECK(reply != nullptr); + LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr"); reply->writeString16(getInterfaceDescriptor()); return NO_ERROR; diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index eb8b79f501..49038b1974 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -23,7 +23,6 @@ #include <binder/IResultReceiver.h> #include <binder/RpcSession.h> #include <binder/Stability.h> -#include <utils/Log.h> #include <stdio.h> diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index c6e4fb378d..7ae616e2b0 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -19,8 +19,6 @@ #include <binder/ProcessState.h> -#include <utils/misc.h> - #include <stdio.h> #include <stdlib.h> #include <ctype.h> diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp index 8ee6cb0615..895690f9b8 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -21,12 +21,15 @@ #include <poll.h> -#include <android-base/macros.h> -#include <android-base/scopeguard.h> +#include <binder/Functional.h> #include "RpcState.h" +#include "Utils.h" + namespace android { +using namespace android::binder::impl; + std::unique_ptr<FdTrigger> FdTrigger::make() { auto ret = std::make_unique<FdTrigger>(); #ifndef BINDER_RPC_SINGLE_THREADED @@ -50,7 +53,7 @@ bool FdTrigger::isTriggered() { #ifdef BINDER_RPC_SINGLE_THREADED return mTriggered; #else - return mWrite == -1; + return !mWrite.ok(); #endif } @@ -74,10 +77,9 @@ status_t FdTrigger::triggerablePoll(const android::RpcTransportFd& transportFd, "Only one thread should be polling on Fd!"); transportFd.setPollingState(true); - auto pollingStateGuard = - android::base::make_scope_guard([&]() { transportFd.setPollingState(false); }); + auto pollingStateGuard = make_scope_guard([&]() { transportFd.setPollingState(false); }); - int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); + int ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1)); if (ret < 0) { return -errno; } diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h index 5fbf2908ad..dba1dc9f6d 100644 --- a/libs/binder/FdTrigger.h +++ b/libs/binder/FdTrigger.h @@ -17,7 +17,6 @@ #include <memory> -#include <android-base/result.h> #include <android-base/unique_fd.h> #include <utils/Errors.h> diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp index 2780bd4cd9..dea26038cf 100644 --- a/libs/binder/IInterface.cpp +++ b/libs/binder/IInterface.cpp @@ -15,7 +15,6 @@ */ #define LOG_TAG "IInterface" -#include <utils/Log.h> #include <binder/IInterface.h> namespace android { diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index da58251149..9341eff91e 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -22,7 +22,6 @@ #include <binder/BpBinder.h> #include <binder/TextOutput.h> -#include <android-base/macros.h> #include <cutils/sched_policy.h> #include <utils/CallStack.h> #include <utils/Log.h> @@ -395,7 +394,9 @@ void IPCThreadState::restoreGetCallingSpGuard(const SpGuard* guard) { } void IPCThreadState::checkContextIsBinderForUse(const char* use) const { - if (LIKELY(mServingStackPointerGuard == nullptr)) return; + if (mServingStackPointerGuard == nullptr) [[likely]] { + return; + } if (!mServingStackPointer || mServingStackPointerGuard->address < mServingStackPointer) { LOG_ALWAYS_FATAL("In context %s, %s does not make sense (binder sp: %p, guard: %p).", @@ -832,7 +833,7 @@ status_t IPCThreadState::transact(int32_t handle, } if ((flags & TF_ONE_WAY) == 0) { - if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) { + if (mCallRestriction != ProcessState::CallRestriction::NONE) [[unlikely]] { if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) { ALOGE("Process making non-oneway call (code: %u) but is restricted.", code); CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(), @@ -842,13 +843,13 @@ status_t IPCThreadState::transact(int32_t handle, } } - #if 0 +#if 0 if (code == 4) { // relayout ALOGI(">>>>>> CALLING transaction 4"); } else { ALOGI(">>>>>> CALLING transaction %d", code); } - #endif +#endif if (reply) { err = waitForResponse(reply); } else { diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp index cd92217f84..60ece7269c 100644 --- a/libs/binder/IResultReceiver.cpp +++ b/libs/binder/IResultReceiver.cpp @@ -18,7 +18,6 @@ #include <binder/IResultReceiver.h> -#include <utils/Log.h> #include <binder/Parcel.h> #include <utils/String8.h> diff --git a/libs/binder/OS.h b/libs/binder/OS.h index 8dc1f6ae70..bb7caa951b 100644 --- a/libs/binder/OS.h +++ b/libs/binder/OS.h @@ -18,14 +18,13 @@ #include <stddef.h> #include <cstdint> -#include <android-base/result.h> #include <android-base/unique_fd.h> #include <binder/RpcTransport.h> #include <utils/Errors.h> namespace android::binder::os { -android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); +status_t setNonBlocking(android::base::borrowed_fd fd); status_t getRandomBytes(uint8_t* data, size_t size); diff --git a/libs/binder/OS_unix_base.cpp b/libs/binder/OS_unix_base.cpp index 81933d5c6b..a3cf117326 100644 --- a/libs/binder/OS_unix_base.cpp +++ b/libs/binder/OS_unix_base.cpp @@ -15,29 +15,29 @@ */ #include "OS.h" +#include "Utils.h" #include <android-base/file.h> #include <binder/RpcTransportRaw.h> #include <log/log.h> #include <string.h> -using android::base::ErrnoError; -using android::base::Result; - namespace android::binder::os { // Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets. constexpr size_t kMaxFdsPerMsg = 253; -Result<void> setNonBlocking(android::base::borrowed_fd fd) { +status_t setNonBlocking(android::base::borrowed_fd fd) { int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); if (flags == -1) { - return ErrnoError() << "Could not get flags for fd"; + PLOGE("Failed setNonBlocking: Could not get flags for fd"); + return -errno; } if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) { - return ErrnoError() << "Could not set non-blocking flag for fd"; + PLOGE("Failed setNonBlocking: Could not set non-blocking flag for fd"); + return -errno; } - return {}; + return OK; } status_t getRandomBytes(uint8_t* data, size_t size) { diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index a3ff7d258b..334940298f 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "Parcel" //#define LOG_NDEBUG 0 +#include <endian.h> #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -32,6 +33,7 @@ #include <binder/Binder.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/IPCThreadState.h> #include <binder/Parcel.h> #include <binder/ProcessState.h> @@ -39,15 +41,11 @@ #include <binder/Status.h> #include <binder/TextOutput.h> -#include <android-base/scopeguard.h> #ifndef BINDER_DISABLE_BLOB #include <cutils/ashmem.h> #endif -#include <utils/Flattenable.h> -#include <utils/Log.h> #include <utils/String16.h> #include <utils/String8.h> -#include <utils/misc.h> #include "OS.h" #include "RpcState.h" @@ -98,6 +96,8 @@ static size_t pad_size(size_t s) { namespace android { +using namespace android::binder::impl; + // many things compile this into prebuilts on the stack #ifdef __LP64__ static_assert(sizeof(Parcel) == 120); @@ -627,7 +627,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } const size_t savedDataPos = mDataPos; - base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; }; + auto scopeGuard = make_scope_guard([&]() { mDataPos = savedDataPos; }); rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size()); if (otherRpcFields->mFds != nullptr) { diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 58203c1373..0344eb04d6 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -18,10 +18,9 @@ #include <binder/ProcessState.h> -#include <android-base/result.h> -#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/Stability.h> @@ -32,6 +31,7 @@ #include <utils/Thread.h> #include "Static.h" +#include "Utils.h" #include "binder_module.h" #include <errno.h> @@ -60,6 +60,8 @@ const char* kDefaultDriver = "/dev/binder"; namespace android { +using namespace android::binder::impl; + class PoolThread : public Thread { public: @@ -430,7 +432,7 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { size_t ProcessState::getThreadPoolMaxTotalThreadCount() const { pthread_mutex_lock(&mThreadCountLock); - base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); }; + auto detachGuard = make_scope_guard([&]() { pthread_mutex_unlock(&mThreadCountLock); }); if (mThreadPoolStarted) { LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1, @@ -512,31 +514,31 @@ String8 ProcessState::getDriverName() { return mDriverName; } -static base::Result<int> open_driver(const char* driver) { - int fd = open(driver, O_RDWR | O_CLOEXEC); - if (fd < 0) { - return base::ErrnoError() << "Opening '" << driver << "' failed"; +static base::unique_fd open_driver(const char* driver) { + auto fd = base::unique_fd(open(driver, O_RDWR | O_CLOEXEC)); + if (!fd.ok()) { + PLOGE("Opening '%s' failed", driver); + return {}; } int vers = 0; - status_t result = ioctl(fd, BINDER_VERSION, &vers); + int result = ioctl(fd.get(), BINDER_VERSION, &vers); if (result == -1) { - close(fd); - return base::ErrnoError() << "Binder ioctl to obtain version failed"; + PLOGE("Binder ioctl to obtain version failed"); + return {}; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { - close(fd); - return base::Error() << "Binder driver protocol(" << vers - << ") does not match user space protocol(" - << BINDER_CURRENT_PROTOCOL_VERSION - << ")! ioctl() return value: " << result; + ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! " + "ioctl() return value: %d", + vers, BINDER_CURRENT_PROTOCOL_VERSION, result); + return {}; } size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; - result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); + result = ioctl(fd.get(), BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION; - result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable); + result = ioctl(fd.get(), BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable); if (result == -1) { ALOGE_IF(ProcessState::isDriverFeatureEnabled( ProcessState::DriverFeature::ONEWAY_SPAM_DETECTION), @@ -561,28 +563,27 @@ ProcessState::ProcessState(const char* driver) mThreadPoolStarted(false), mThreadPoolSeq(1), mCallRestriction(CallRestriction::NONE) { - base::Result<int> opened = open_driver(driver); + base::unique_fd opened = open_driver(driver); if (opened.ok()) { // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, - opened.value(), 0); + opened.get(), 0); if (mVMStart == MAP_FAILED) { - close(opened.value()); // *sigh* - opened = base::Error() - << "Using " << driver << " failed: unable to mmap transaction memory."; + ALOGE("Using %s failed: unable to mmap transaction memory.", driver); + opened.reset(); mDriverName.clear(); } } #ifdef __ANDROID__ - LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating: %s", - driver, opened.error().message().c_str()); + LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating.", + driver); #endif if (opened.ok()) { - mDriverFD = opened.value(); + mDriverFD = opened.release(); } } diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp index 324670633f..f7b5a55a31 100644 --- a/libs/binder/RecordedTransaction.cpp +++ b/libs/binder/RecordedTransaction.cpp @@ -15,13 +15,14 @@ */ #include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/scopeguard.h> #include <android-base/unique_fd.h> +#include <binder/Functional.h> #include <binder/RecordedTransaction.h> +#include <inttypes.h> #include <sys/mman.h> #include <algorithm> +using namespace android::binder::impl; using android::Parcel; using android::base::borrowed_fd; using android::base::unique_fd; @@ -126,18 +127,17 @@ std::optional<RecordedTransaction> RecordedTransaction::fromDetails( t.mData.mInterfaceName = std::string(String8(interfaceName).c_str()); if (interfaceName.size() != t.mData.mInterfaceName.size()) { - LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte " - "utf-8."; + ALOGE("Interface Name is not valid. Contains characters that aren't single byte utf-8."); return std::nullopt; } if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) { - LOG(ERROR) << "Failed to set sent parcel data."; + ALOGE("Failed to set sent parcel data."); return std::nullopt; } if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) { - LOG(ERROR) << "Failed to set reply parcel data."; + ALOGE("Failed to set reply parcel data."); return std::nullopt; } @@ -167,38 +167,37 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd const long pageSize = sysconf(_SC_PAGE_SIZE); struct stat fileStat; if (fstat(fd.get(), &fileStat) != 0) { - LOG(ERROR) << "Unable to get file information"; + ALOGE("Unable to get file information"); return std::nullopt; } off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); if (fdCurrentPosition == -1) { - LOG(ERROR) << "Invalid offset in file descriptor."; + ALOGE("Invalid offset in file descriptor."); return std::nullopt; } do { if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) { - LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor"; + ALOGE("Not enough file remains to contain expected chunk descriptor"); return std::nullopt; } if (!android::base::ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) { - LOG(ERROR) << "Failed to read ChunkDescriptor from fd " << fd.get() << ". " - << strerror(errno); + ALOGE("Failed to read ChunkDescriptor from fd %d. %s", fd.get(), strerror(errno)); return std::nullopt; } transaction_checksum_t checksum = *reinterpret_cast<transaction_checksum_t*>(&chunk); fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); if (fdCurrentPosition == -1) { - LOG(ERROR) << "Invalid offset in file descriptor."; + ALOGE("Invalid offset in file descriptor."); return std::nullopt; } off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize; off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart; if (chunk.dataSize > kMaxChunkDataSize) { - LOG(ERROR) << "Chunk data exceeds maximum size."; + ALOGE("Chunk data exceeds maximum size."); return std::nullopt; } @@ -206,19 +205,19 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t); if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) { - LOG(ERROR) << "Chunk payload exceeds remaining file size."; + ALOGE("Chunk payload exceeds remaining file size."); return std::nullopt; } if (PADDING8(chunkPayloadSize) != 0) { - LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize; + ALOGE("Invalid chunk size, not aligned %zu", chunkPayloadSize); return std::nullopt; } size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset; void* mappedMemory = mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart); - auto mmap_guard = android::base::make_scope_guard( + auto mmap_guard = make_scope_guard( [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); }); transaction_checksum_t* payloadMap = @@ -227,8 +226,7 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap // page-alignment if (payloadMap == MAP_FAILED) { - LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " " - << strerror(errno); + ALOGE("Memory mapping failed for fd %d: %d %s", fd.get(), errno, strerror(errno)); return std::nullopt; } for (size_t checksumIndex = 0; @@ -236,21 +234,21 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd checksum ^= payloadMap[checksumIndex]; } if (checksum != 0) { - LOG(ERROR) << "Checksum failed."; + ALOGE("Checksum failed."); return std::nullopt; } fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR); if (fdCurrentPosition == -1) { - LOG(ERROR) << "Invalid offset in file descriptor."; + ALOGE("Invalid offset in file descriptor."); return std::nullopt; } switch (chunk.chunkType) { case HEADER_CHUNK: { if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) { - LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected " - << sizeof(TransactionHeader) << "."; + ALOGE("Header Chunk indicated size %" PRIu32 "; Expected %zu.", chunk.dataSize, + sizeof(TransactionHeader)); return std::nullopt; } t.mData.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap); @@ -264,7 +262,7 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd case DATA_PARCEL_CHUNK: { if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap), chunk.dataSize) != android::NO_ERROR) { - LOG(ERROR) << "Failed to set sent parcel data."; + ALOGE("Failed to set sent parcel data."); return std::nullopt; } break; @@ -272,7 +270,7 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd case REPLY_PARCEL_CHUNK: { if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap), chunk.dataSize) != android::NO_ERROR) { - LOG(ERROR) << "Failed to set reply parcel data."; + ALOGE("Failed to set reply parcel data."); return std::nullopt; } break; @@ -280,7 +278,7 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd case END_CHUNK: break; default: - LOG(INFO) << "Unrecognized chunk."; + ALOGI("Unrecognized chunk."); break; } } while (chunk.chunkType != END_CHUNK); @@ -291,7 +289,7 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType, size_t byteCount, const uint8_t* data) const { if (byteCount > kMaxChunkDataSize) { - LOG(ERROR) << "Chunk data exceeds maximum size"; + ALOGE("Chunk data exceeds maximum size"); return BAD_VALUE; } ChunkDescriptor descriptor = {.chunkType = chunkType, @@ -320,7 +318,7 @@ android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunk // Write buffer to file if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) { - LOG(ERROR) << "Failed to write chunk fd " << fd.get(); + ALOGE("Failed to write chunk fd %d", fd.get()); return UNKNOWN_ERROR; } return NO_ERROR; @@ -330,26 +328,26 @@ android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { if (NO_ERROR != writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader), reinterpret_cast<const uint8_t*>(&(mData.mHeader)))) { - LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get(); + ALOGE("Failed to write transactionHeader to fd %d", fd.get()); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t), reinterpret_cast<const uint8_t*>(mData.mInterfaceName.c_str()))) { - LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get(); + ALOGI("Failed to write Interface Name Chunk to fd %d", fd.get()); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) { - LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get(); + ALOGE("Failed to write sent Parcel to fd %d", fd.get()); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) { - LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get(); + ALOGE("Failed to write reply Parcel to fd %d", fd.get()); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) { - LOG(ERROR) << "Failed to write end chunk to fd " << fd.get(); + ALOGE("Failed to write end chunk to fd %d", fd.get()); return UNKNOWN_ERROR; } return NO_ERROR; diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 07ab093992..fefaa810d2 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -25,12 +25,11 @@ #include <thread> #include <vector> -#include <android-base/scopeguard.h> +#include <binder/Functional.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <log/log.h> -#include <utils/Compat.h> #include "BuildFlags.h" #include "FdTrigger.h" @@ -45,7 +44,7 @@ namespace android { constexpr size_t kSessionIdBytes = 32; -using base::ScopeGuard; +using namespace android::binder::impl; using base::unique_fd; RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {} @@ -169,7 +168,7 @@ void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& f void RpcServer::setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier) { RpcMutexLockGuard _l(mLock); - LOG_ALWAYS_FATAL_IF(mServer.fd != -1, "Already started"); + LOG_ALWAYS_FATAL_IF(mServer.fd.ok(), "Already started"); mServerSocketModifier = std::move(modifier); } @@ -201,7 +200,7 @@ void RpcServer::start() { status_t RpcServer::acceptSocketConnection(const RpcServer& server, RpcTransportFd* out) { RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY( accept4(server.mServer.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK)))); - if (clientSocket.fd < 0) { + if (!clientSocket.fd.ok()) { int savedErrno = errno; ALOGE("Could not accept4 socket: %s", strerror(savedErrno)); return -savedErrno; @@ -231,10 +230,7 @@ status_t RpcServer::recvmsgSocketConnection(const RpcServer& server, RpcTranspor } unique_fd fd(std::move(std::get<unique_fd>(fds.back()))); - if (auto res = binder::os::setNonBlocking(fd); !res.ok()) { - ALOGE("Failed setNonBlocking: %s", res.error().message().c_str()); - return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code(); - } + if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res; *out = RpcTransportFd(std::move(fd)); return OK; @@ -458,11 +454,12 @@ void RpcServer::establishConnection( LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(), "Must establish connection on owned thread"); thisThread = std::move(threadId->second); - ScopeGuard detachGuard = [&]() { + auto detachGuardLambda = [&]() { thisThread.detach(); _l.unlock(); server->mShutdownCv.notify_all(); }; + auto detachGuard = make_scope_guard(std::ref(detachGuardLambda)); server->mConnectingThreads.erase(threadId); if (status != OK || server->mShutdownTrigger->isTriggered()) { @@ -548,7 +545,7 @@ void RpcServer::establishConnection( return; } - detachGuard.Disable(); + detachGuard.release(); session->preJoinThreadOwnership(std::move(thisThread)); } diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index fa8f2b51ac..cd8f41711f 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -26,14 +26,12 @@ #include <string_view> -#include <android-base/macros.h> -#include <android-base/scopeguard.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <binder/Stability.h> -#include <utils/Compat.h> #include <utils/String8.h> #include "BuildFlags.h" @@ -52,6 +50,7 @@ extern "C" JavaVM* AndroidRuntimeGetJavaVM(); namespace android { +using namespace android::binder::impl; using base::unique_fd; RpcSession::RpcSession(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) { @@ -194,10 +193,7 @@ status_t RpcSession::setupPreconnectedClient(base::unique_fd fd, fd = request(); if (!fd.ok()) return BAD_VALUE; } - if (auto res = binder::os::setNonBlocking(fd); !res.ok()) { - ALOGE("setupPreconnectedClient: %s", res.error().message().c_str()); - return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code(); - } + if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res; RpcTransportFd transportFd(std::move(fd)); status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming); @@ -212,7 +208,7 @@ status_t RpcSession::addNullDebuggingClient() { unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC))); - if (serverFd == -1) { + if (!serverFd.ok()) { int savedErrno = errno; ALOGE("Could not connect to /dev/null: %s", strerror(savedErrno)); return -savedErrno; @@ -412,7 +408,9 @@ public: } private: - DISALLOW_COPY_AND_ASSIGN(JavaThreadAttacher); + JavaThreadAttacher(const JavaThreadAttacher&) = delete; + void operator=(const JavaThreadAttacher&) = delete; + bool mAttached = false; static JavaVM* getJavaVM() { @@ -497,7 +495,7 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< if (auto status = initShutdownTrigger(); status != OK) return status; auto oldProtocolVersion = mProtocolVersion; - auto cleanup = base::ScopeGuard([&] { + auto cleanup = make_scope_guard([&] { // if any threads are started, shut them down (void)shutdownAndWait(true); @@ -577,7 +575,7 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< if (status_t status = connectAndInit(mId, true /*incoming*/); status != OK) return status; } - cleanup.Disable(); + cleanup.release(); return OK; } @@ -596,7 +594,7 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, unique_fd serverFd(TEMP_FAILURE_RETRY( socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); - if (serverFd == -1) { + if (!serverFd.ok()) { int savedErrno = errno; ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 749c2f81ea..008e5d21f0 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -18,9 +18,8 @@ #include "RpcState.h" -#include <android-base/macros.h> -#include <android-base/scopeguard.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/IPCThreadState.h> #include <binder/RpcServer.h> @@ -39,6 +38,8 @@ namespace android { +using namespace android::binder::impl; + #if RPC_FLAKE_PRONE void rpcMaybeWaitToFlake() { [[clang::no_destroy]] static std::random_device r; @@ -357,7 +358,7 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { status_t RpcState::rpcSend( const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { for (int i = 0; i < niovs; i++) { LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s", @@ -600,25 +601,24 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti {const_cast<uint8_t*>(data.data()), data.dataSize()}, objectTableSpan.toIovec(), }; - if (status_t status = rpcSend( - connection, session, "transaction", iovs, arraysize(iovs), - [&] { - if (waitUs > kWaitLogUs) { - ALOGE("Cannot send command, trying to process pending refcounts. Waiting " - "%zuus. Too many oneway calls?", - waitUs); - } + auto altPoll = [&] { + if (waitUs > kWaitLogUs) { + ALOGE("Cannot send command, trying to process pending refcounts. Waiting " + "%zuus. Too many oneway calls?", + waitUs); + } - if (waitUs > 0) { - usleep(waitUs); - waitUs = std::min(kWaitMaxUs, waitUs * 2); - } else { - waitUs = 1; - } + if (waitUs > 0) { + usleep(waitUs); + waitUs = std::min(kWaitMaxUs, waitUs * 2); + } else { + waitUs = 1; + } - return drainCommands(connection, session, CommandType::CONTROL_ONLY); - }, - rpcFields->mFds.get()); + return drainCommands(connection, session, CommandType::CONTROL_ONLY); + }; + if (status_t status = rpcSend(connection, session, "transaction", iovs, countof(iovs), + std::ref(altPoll), rpcFields->mFds.get()); status != OK) { // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate // errors here, then we may need to undo the binder-sent counts for the transaction as @@ -690,7 +690,7 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection, {&rpcReply, rpcReplyWireSize}, {data.data(), data.size()}, }; - if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr); + if (status_t status = rpcRec(connection, session, "reply body", iovs, countof(iovs), nullptr); status != OK) return status; @@ -760,7 +760,7 @@ status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& co .bodySize = sizeof(RpcDecStrong), }; iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}}; - return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt); + return rpcSend(connection, session, "dec ref", iovs, countof(iovs), std::nullopt); } status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection, @@ -809,11 +809,11 @@ status_t RpcState::processCommand( origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard); } - base::ScopeGuard guardUnguard = [&]() { + auto guardUnguard = make_scope_guard([&]() { if (kernelBinderState != nullptr) { kernelBinderState->restoreGetCallingSpGuard(origGuard); } - }; + }); #endif // BINDER_WITH_KERNEL_IPC switch (command.command) { @@ -1143,7 +1143,7 @@ processTransactInternalTailCall: {const_cast<uint8_t*>(reply.data()), reply.dataSize()}, objectTableSpan.toIovec(), }; - return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt, + return rpcSend(connection, session, "reply", iovs, countof(iovs), std::nullopt, rpcFields->mFds.get()); } diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index 1fe71a5a78..2a954e632b 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -16,6 +16,7 @@ #pragma once #include <android-base/unique_fd.h> +#include <binder/Functional.h> #include <binder/IBinder.h> #include <binder/Parcel.h> #include <binder/RpcSession.h> @@ -190,7 +191,7 @@ private: [[nodiscard]] status_t rpcSend( const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr); [[nodiscard]] status_t rpcRec( diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index c089811561..ffa315191d 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -29,6 +29,8 @@ namespace android { +using namespace android::binder::impl; + // RpcTransport with TLS disabled. class RpcTransportRaw : public RpcTransport { public: @@ -54,7 +56,7 @@ public: status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { bool sentFds = false; @@ -70,7 +72,7 @@ public: status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { auto recv = [&](iovec* iovs, int niovs) -> ssize_t { return binder::os::receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds); diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp index 0c81d83032..188ba3b0cb 100644 --- a/libs/binder/RpcTransportTipcAndroid.cpp +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -26,8 +26,7 @@ #include "RpcState.h" #include "RpcTransportUtils.h" -using android::base::Error; -using android::base::Result; +using namespace android::binder::impl; namespace android { @@ -75,7 +74,7 @@ public: status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t { @@ -93,7 +92,7 @@ public: status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/) override { auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t { diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index efb09e9004..fef4be4545 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -29,6 +29,8 @@ #include "RpcState.h" #include "Utils.h" +#include <sstream> + #define SHOULD_LOG_TLS_DETAIL false #if SHOULD_LOG_TLS_DETAIL @@ -38,6 +40,9 @@ #endif namespace android { + +using namespace android::binder::impl; + namespace { // Implement BIO for socket that ignores SIGPIPE. @@ -181,10 +186,9 @@ public: // |sslError| should be from Ssl::getError(). // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise // return error. Also return error if |fdTrigger| is triggered before or during poll(). - status_t pollForSslError( - const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger, - const char* fnString, int additionalEvent, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { + status_t pollForSslError(const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger, + const char* fnString, int additionalEvent, + const std::optional<SmallFunction<status_t()>>& altPoll) { switch (sslError) { case SSL_ERROR_WANT_READ: return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); @@ -200,7 +204,7 @@ private: status_t handlePoll(int event, const android::RpcTransportFd& fd, FdTrigger* fdTrigger, const char* fnString, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { + const std::optional<SmallFunction<status_t()>>& altPoll) { status_t ret; if (altPoll) { ret = (*altPoll)(); @@ -284,12 +288,12 @@ public: status_t pollRead(void) override; status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override; status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override; bool isWaiting() override { return mSocket.isInPollingState(); }; @@ -320,7 +324,7 @@ status_t RpcTransportTls::pollRead(void) { status_t RpcTransportTls::interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { (void)ancillaryFds; @@ -366,7 +370,7 @@ status_t RpcTransportTls::interruptableWriteFully( status_t RpcTransportTls::interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { (void)ancillaryFds; diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h index 32f0db805d..a0e502e92a 100644 --- a/libs/binder/RpcTransportUtils.h +++ b/libs/binder/RpcTransportUtils.h @@ -27,7 +27,7 @@ template <typename SendOrReceive> status_t interruptableReadOrWrite( const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs, SendOrReceive sendOrReceiveFun, const char* funName, int16_t event, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { + const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll) { MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) { diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp index 3b53b05991..2f2fe7d276 100644 --- a/libs/binder/RpcTrusty.cpp +++ b/libs/binder/RpcTrusty.cpp @@ -16,7 +16,6 @@ #define LOG_TAG "RpcTrusty" -#include <android-base/logging.h> #include <android-base/unique_fd.h> #include <binder/RpcSession.h> #include <binder/RpcTransportTipcAndroid.h> @@ -35,13 +34,13 @@ sp<RpcSession> RpcTrustyConnectWithSessionInitializer( auto request = [=] { int tipcFd = tipc_connect(device, port); if (tipcFd < 0) { - LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd; + ALOGE("Failed to connect to Trusty service. Error code: %d", tipcFd); return unique_fd(); } return unique_fd(tipcFd); }; if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { - LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str(); + ALOGE("Failed to set up Trusty client. Error: %s", statusToString(status).c_str()); return nullptr; } return session; diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp index 2b67f030e0..9482e3e118 100644 --- a/libs/binder/ServiceManagerHost.cpp +++ b/libs/binder/ServiceManagerHost.cpp @@ -56,16 +56,16 @@ public: [[nodiscard]] const std::optional<unsigned int>& hostPort() const { return mPort; } private: - DISALLOW_COPY_AND_ASSIGN(AdbForwarder); + AdbForwarder(const AdbForwarder&) = delete; + void operator=(const AdbForwarder&) = delete; explicit AdbForwarder(unsigned int port) : mPort(port) {} std::optional<unsigned int> mPort; }; std::optional<AdbForwarder> AdbForwarder::forward(unsigned int devicePort) { auto result = execute({"adb", "forward", "tcp:0", "tcp:" + std::to_string(devicePort)}, nullptr); - if (!result.ok()) { - ALOGE("Unable to run `adb forward tcp:0 tcp:%d`: %s", devicePort, - result.error().message().c_str()); + if (!result.has_value()) { + ALOGE("Unable to run `adb forward tcp:0 tcp:%d`", devicePort); return std::nullopt; } // Must end with exit code 0 (`has_value() && value() == 0`) @@ -94,9 +94,8 @@ AdbForwarder::~AdbForwarder() { if (!mPort.has_value()) return; auto result = execute({"adb", "forward", "--remove", "tcp:" + std::to_string(*mPort)}, nullptr); - if (!result.ok()) { - ALOGE("Unable to run `adb forward --remove tcp:%d`: %s", *mPort, - result.error().message().c_str()); + if (!result.has_value()) { + ALOGE("Unable to run `adb forward --remove tcp:%d`", *mPort); return; } // Must end with exit code 0 (`has_value() && value() == 0`) @@ -130,8 +129,7 @@ sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs, serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end()); auto result = execute(std::move(serviceDispatcherArgs), &CommandResult::stdoutEndsWithNewLine); - if (!result.ok()) { - ALOGE("%s", result.error().message().c_str()); + if (!result.has_value()) { return nullptr; } diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h index c5310dac20..941ba3a757 100644 --- a/libs/binder/ServiceManagerHost.h +++ b/libs/binder/ServiceManagerHost.h @@ -16,7 +16,6 @@ #pragma once -#include <android-base/macros.h> #include <android/os/IServiceManager.h> namespace android { diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp index 47fd17dcb1..d9a96af80a 100644 --- a/libs/binder/Utils.cpp +++ b/libs/binder/Utils.cpp @@ -16,7 +16,6 @@ #include "Utils.h" -#include <android-base/logging.h> #include <string.h> namespace android { @@ -26,7 +25,7 @@ void zeroMemory(uint8_t* data, size_t size) { } std::string HexString(const void* bytes, size_t len) { - CHECK(bytes != nullptr || len == 0) << bytes << " " << len; + LOG_ALWAYS_FATAL_IF(len > 0 && bytes == nullptr, "%p %zu", bytes, len); // b/132916539: Doing this the 'C way', std::setfill triggers ubsan implicit conversion const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes); diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h index 8942c31c48..eec09eb859 100644 --- a/libs/binder/Utils.h +++ b/libs/binder/Utils.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + #include <stddef.h> #include <sys/uio.h> #include <cstdint> @@ -22,6 +24,17 @@ #include <log/log.h> #include <utils/Errors.h> +#define PLOGE(fmt, ...) \ + do { \ + auto savedErrno = errno; \ + ALOGE(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \ + } while (0) +#define PLOGF(fmt, ...) \ + do { \ + auto savedErrno = errno; \ + LOG_ALWAYS_FATAL(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \ + } while (0) + /* TEMP_FAILURE_RETRY is not available on macOS and Trusty. */ #ifndef TEMP_FAILURE_RETRY /* Used to retry syscalls that can return EINTR. */ @@ -45,6 +58,17 @@ namespace android { +/** + * Get the size of a statically initialized array. + * + * \param N the array to get the size of. + * \return the size of the array. + */ +template <typename T, size_t N> +constexpr size_t countof(T (&)[N]) { + return N; +} + // avoid optimizations void zeroMemory(uint8_t* data, size_t size); diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp index 52b8f69d36..3db038f03f 100644 --- a/libs/binder/UtilsHost.cpp +++ b/libs/binder/UtilsHost.cpp @@ -25,6 +25,8 @@ #include <log/log.h> +#include "Utils.h" + namespace android { CommandResult::~CommandResult() { @@ -72,8 +74,8 @@ std::string CommandResult::toString() const { return ss.str(); } -android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec, - const std::function<bool(const CommandResult&)>& end) { +std::optional<CommandResult> execute(std::vector<std::string> argStringVec, + const std::function<bool(const CommandResult&)>& end) { // turn vector<string> into null-terminated char* vector. std::vector<char*> argv; argv.reserve(argStringVec.size() + 1); @@ -82,14 +84,21 @@ android::base::Result<CommandResult> execute(std::vector<std::string> argStringV CommandResult ret; android::base::unique_fd outWrite; - if (!android::base::Pipe(&ret.outPipe, &outWrite)) - return android::base::ErrnoError() << "pipe() for outPipe"; + if (!android::base::Pipe(&ret.outPipe, &outWrite)) { + PLOGE("pipe() for outPipe"); + return {}; + } android::base::unique_fd errWrite; - if (!android::base::Pipe(&ret.errPipe, &errWrite)) - return android::base::ErrnoError() << "pipe() for errPipe"; + if (!android::base::Pipe(&ret.errPipe, &errWrite)) { + PLOGE("pipe() for errPipe"); + return {}; + } int pid = fork(); - if (pid == -1) return android::base::ErrnoError() << "fork()"; + if (pid == -1) { + PLOGE("fork()"); + return {}; + } if (pid == 0) { // child ret.outPipe.reset(); @@ -140,12 +149,19 @@ android::base::Result<CommandResult> execute(std::vector<std::string> argStringV *errPollFd = {.fd = ret.errPipe.get(), .events = POLLIN}; } int pollRet = poll(fds, nfds, 1000 /* ms timeout */); - if (pollRet == -1) return android::base::ErrnoError() << "poll()"; + if (pollRet == -1) { + PLOGE("poll()"); + return {}; + } - if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) - return android::base::ErrnoError() << "read(stdout)"; - if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) - return android::base::ErrnoError() << "read(stderr)"; + if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) { + PLOGE("read(stdout)"); + return {}; + } + if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) { + PLOGE("read(stderr)"); + return {}; + } if (end && end(ret)) return ret; } @@ -154,7 +170,10 @@ android::base::Result<CommandResult> execute(std::vector<std::string> argStringV while (ret.pid.has_value()) { int status; auto exitPid = waitpid(pid, &status, 0); - if (exitPid == -1) return android::base::ErrnoError() << "waitpid(" << pid << ")"; + if (exitPid == -1) { + PLOGE("waitpid(%d)", pid); + return {}; + } if (exitPid == pid) { if (WIFEXITED(status)) { ret.pid = std::nullopt; diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h index 98ac4e0c48..5de0980d8e 100644 --- a/libs/binder/UtilsHost.h +++ b/libs/binder/UtilsHost.h @@ -23,8 +23,8 @@ #include <vector> #include <android-base/macros.h> -#include <android-base/result.h> #include <android-base/unique_fd.h> +#include <utils/Errors.h> /** * Log a lot more information about host-device binder communication, when debugging issues. @@ -67,7 +67,8 @@ struct CommandResult { } private: - DISALLOW_COPY_AND_ASSIGN(CommandResult); + CommandResult(const CommandResult&) = delete; + void operator=(const CommandResult&) = delete; }; std::ostream& operator<<(std::ostream& os, const CommandResult& res); @@ -94,6 +95,6 @@ std::ostream& operator<<(std::ostream& os, const CommandResult& res); // // If the parent process has encountered any errors for system calls, return ExecuteError with // the proper errno set. -android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec, - const std::function<bool(const CommandResult&)>& end); +std::optional<CommandResult> execute(std::vector<std::string> argStringVec, + const std::function<bool(const CommandResult&)>& end); } // namespace android diff --git a/libs/binder/include/binder/Functional.h b/libs/binder/include/binder/Functional.h new file mode 100644 index 0000000000..08e3b214da --- /dev/null +++ b/libs/binder/include/binder/Functional.h @@ -0,0 +1,50 @@ +/* + * 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 <functional> +#include <memory> + +namespace android::binder::impl { + +template <typename F> +constexpr void assert_small_callable() { + // While this buffer (std::function::__func::__buf_) is an implementation detail generally not + // accessible to users, it's a good bet to assume its size to be around 3 pointers. + constexpr size_t kFunctionBufferSize = 3 * sizeof(void*); + + static_assert(sizeof(F) <= kFunctionBufferSize, + "Supplied callable is larger than std::function optimization buffer. " + "Try using std::ref, but make sure lambda lives long enough to be called."); +} + +template <typename F> +std::unique_ptr<void, std::function<void(void*)>> make_scope_guard(F&& f) { + assert_small_callable<decltype(std::bind(f))>(); + return {reinterpret_cast<void*>(true), std::bind(f)}; +} + +template <typename T> +class SmallFunction : public std::function<T> { +public: + template <typename F> + SmallFunction(F&& f) : std::function<T>(f) { + assert_small_callable<F>(); + } +}; + +} // namespace android::binder::impl diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 98d12bb120..6961abc64b 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -33,7 +33,6 @@ #include <utils/RefBase.h> #include <utils/String16.h> #include <utils/Vector.h> -#include <utils/Flattenable.h> #include <binder/IInterface.h> #include <binder/Parcelable.h> diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 363b5e115e..3672702fe1 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -17,7 +17,6 @@ #pragma once #include <binder/IBinder.h> -#include <utils/KeyedVector.h> #include <utils/String16.h> #include <utils/String8.h> diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index cb6460398d..e3805ac888 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -15,7 +15,6 @@ */ #pragma once -#include <android-base/threads.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/RpcThreads.h> diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index 6db9ad983c..115a1732d7 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -25,10 +25,10 @@ #include <variant> #include <vector> -#include <android-base/function_ref.h> #include <android-base/unique_fd.h> #include <utils/Errors.h> +#include <binder/Functional.h> #include <binder/RpcCertificateFormat.h> #include <binder/RpcThreads.h> @@ -85,13 +85,13 @@ public: * error - interrupted (failure or trigger) */ [[nodiscard]] virtual status_t interruptableWriteFully( - FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>> &altPoll, - const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) = 0; [[nodiscard]] virtual status_t interruptableReadFully( - FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>> &altPoll, - std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) = 0; /** * Check whether any threads are blocked while polling the transport diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index f51cd9bc99..118409eeff 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -16,7 +16,6 @@ #include <binder_rpc_unstable.hpp> -#include <android-base/logging.h> #include <android-base/unique_fd.h> #include <android/binder_libbinder.h> #include <binder/RpcServer.h> @@ -85,8 +84,8 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in } if (status_t status = server->setupVsockServer(bindCid, port); status != OK) { - LOG(ERROR) << "Failed to set up vsock server with port " << port - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up vsock server with port %u error: %s", port, + statusToString(status).c_str()); return nullptr; } if (cid != VMADDR_CID_ANY) { @@ -95,7 +94,7 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr); LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock"); if (cid != vaddr->svm_cid) { - LOG(ERROR) << "Rejected vsock connection from CID " << vaddr->svm_cid; + ALOGE("Rejected vsock connection from CID %u", vaddr->svm_cid); return false; } return true; @@ -109,12 +108,12 @@ ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd) { auto server = RpcServer::make(); auto fd = unique_fd(socketFd); if (!fd.ok()) { - LOG(ERROR) << "Invalid socket fd " << socketFd; + ALOGE("Invalid socket fd %d", socketFd); return nullptr; } if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) { - LOG(ERROR) << "Failed to set up RPC server with fd " << socketFd - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up RPC server with fd %d error: %s", socketFd, + statusToString(status).c_str()); return nullptr; } server->setRootObject(AIBinder_toPlatformBinder(service)); @@ -125,13 +124,13 @@ ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd auto server = RpcServer::make(); auto fd = unique_fd(bootstrapFd); if (!fd.ok()) { - LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd; + ALOGE("Invalid bootstrap fd %d", bootstrapFd); return nullptr; } if (status_t status = server->setupUnixDomainSocketBootstrapServer(std::move(fd)); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC server with bootstrap fd " << bootstrapFd - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up Unix Domain RPC server with bootstrap fd %d error: %s", bootstrapFd, + statusToString(status).c_str()); return nullptr; } server->setRootObject(AIBinder_toPlatformBinder(service)); @@ -141,8 +140,8 @@ ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address, unsigned int port) { auto server = RpcServer::make(); if (status_t status = server->setupInetServer(address, port, nullptr); status != OK) { - LOG(ERROR) << "Failed to set up inet RPC server with address " << address << " and port " - << port << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up inet RPC server with address %s and port %u error: %s", address, + port, statusToString(status).c_str()); return nullptr; } server->setRootObject(AIBinder_toPlatformBinder(service)); @@ -191,8 +190,8 @@ void ARpcSession_free(ARpcSession* handle) { AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) { auto session = handleToStrongPointer<RpcSession>(handle); if (status_t status = session->setupVsockClient(cid, port); status != OK) { - LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up vsock client with CID %u and port %u error: %s", cid, port, + statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); @@ -203,8 +202,8 @@ AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* handle, const char* nam pathname = ANDROID_SOCKET_DIR "/" + pathname; auto session = handleToStrongPointer<RpcSession>(handle); if (status_t status = session->setupUnixDomainClient(pathname.c_str()); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC client with path: " << pathname - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up Unix Domain RPC client with path: %s error: %s", pathname.c_str(), + statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); @@ -214,13 +213,13 @@ AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bo auto session = handleToStrongPointer<RpcSession>(handle); auto fd = unique_fd(dup(bootstrapFd)); if (!fd.ok()) { - LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd; + ALOGE("Invalid bootstrap fd %d", bootstrapFd); return nullptr; } if (status_t status = session->setupUnixDomainSocketBootstrapClient(std::move(fd)); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC client with bootstrap fd: " << bootstrapFd - << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up Unix Domain RPC client with bootstrap fd: %d error: %s", + bootstrapFd, statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); @@ -229,8 +228,8 @@ AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bo AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsigned int port) { auto session = handleToStrongPointer<RpcSession>(handle); if (status_t status = session->setupInetClient(address, port); status != OK) { - LOG(ERROR) << "Failed to set up inet RPC client with address " << address << " and port " - << port << " error: " << statusToString(status).c_str(); + ALOGE("Failed to set up inet RPC client with address %s and port %u error: %s", address, + port, statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); @@ -241,7 +240,7 @@ AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*request auto session = handleToStrongPointer<RpcSession>(handle); auto request = [=] { return unique_fd{requestFd(param)}; }; if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { - LOG(ERROR) << "Failed to set up vsock client. error: " << statusToString(status).c_str(); + ALOGE("Failed to set up vsock client. error: %s", statusToString(status).c_str()); return nullptr; } return AIBinder_fromPlatformBinder(session->getRootObject()); diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 58ed4188e2..47b9f5868d 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -60,6 +60,7 @@ cc_library { "libbinder.cpp", "parcel.cpp", "parcel_jni.cpp", + "persistable_bundle.cpp", "process.cpp", "stability.cpp", "status.cpp", diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 47da296b70..bf7a0ba5f0 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <android-base/logging.h> #include <android/binder_ibinder.h> #include <android/binder_ibinder_platform.h> #include <android/binder_stability.h> @@ -48,8 +47,8 @@ static void* kValue = static_cast<void*>(new bool{true}); void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */}; static void attach(const sp<IBinder>& binder) { - // can only attach once - CHECK_EQ(nullptr, binder->attachObject(kId, kValue, nullptr /*cookie*/, clean)); + auto alreadyAttached = binder->attachObject(kId, kValue, nullptr /*cookie*/, clean); + LOG_ALWAYS_FATAL_IF(alreadyAttached != nullptr, "can only attach once"); } static bool has(const sp<IBinder>& binder) { return binder != nullptr && binder->findObject(kId) == kValue; @@ -65,9 +64,9 @@ struct Value { }; void clean(const void* id, void* obj, void* cookie) { // be weary of leaks! - // LOG(INFO) << "Deleting an ABpBinder"; + // ALOGI("Deleting an ABpBinder"); - CHECK(id == kId) << id << " " << obj << " " << cookie; + LOG_ALWAYS_FATAL_IF(id != kId, "%p %p %p", id, obj, cookie); delete static_cast<Value*>(obj); }; @@ -121,14 +120,13 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (mClazz != nullptr && !asABpBinder()) { const String16& currentDescriptor = mClazz->getInterfaceDescriptor(); if (newDescriptor == currentDescriptor) { - LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor - << "' match during associateClass, but they are different class objects (" - << clazz << " vs " << mClazz << "). Class descriptor collision?"; + ALOGE("Class descriptors '%s' match during associateClass, but they are different class" + " objects (%p vs %p). Class descriptor collision?", + String8(currentDescriptor).c_str(), clazz, mClazz); } else { - LOG(ERROR) << __func__ - << ": Class cannot be associated on object which already has a class. " - "Trying to associate to '" - << newDescriptor << "' but already set to '" << currentDescriptor << "'."; + ALOGE("%s: Class cannot be associated on object which already has a class. " + "Trying to associate to '%s' but already set to '%s'.", + __func__, String8(newDescriptor).c_str(), String8(currentDescriptor).c_str()); } // always a failure because we know mClazz != clazz @@ -141,13 +139,12 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { // more flake-proof. However, the check is not dependent on the lock. if (descriptor != newDescriptor && !(asABpBinder() && asABpBinder()->isServiceFuzzing())) { if (getBinder()->isBinderAlive()) { - LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor - << "' but descriptor is actually '" << SanitizeString(descriptor) << "'."; + ALOGE("%s: Expecting binder to have class '%s' but descriptor is actually '%s'.", + __func__, String8(newDescriptor).c_str(), SanitizeString(descriptor).c_str()); } else { // b/155793159 - LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor - << "' to dead binder with cached descriptor '" << SanitizeString(descriptor) - << "'."; + ALOGE("%s: Cannot associate class '%s' to dead binder with cached descriptor '%s'.", + __func__, String8(newDescriptor).c_str(), SanitizeString(descriptor).c_str()); } return false; } @@ -164,7 +161,7 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData) : AIBinder(clazz), BBinder(), mUserData(userData) { - CHECK(clazz != nullptr); + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "clazz == nullptr"); } ABBinder::~ABBinder() { getClass()->onDestroy(mUserData); @@ -184,7 +181,7 @@ status_t ABBinder::dump(int fd, const ::android::Vector<String16>& args) { // technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be // null in Java if (args.size() > INT32_MAX) { - LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size(); + ALOGE("ABBinder::dump received too many arguments: %zu", args.size()); return STATUS_BAD_VALUE; } @@ -263,7 +260,7 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder) : AIBinder(nullptr /*clazz*/), mRemote(binder) { - CHECK(binder != nullptr); + LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr"); } ABpBinder::~ABpBinder() {} @@ -373,27 +370,27 @@ AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, } void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { - CHECK(clazz != nullptr) << "setOnDump requires non-null clazz"; + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "setOnDump requires non-null clazz"); // this is required to be called before instances are instantiated clazz->onDump = onDump; } void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) { - CHECK(clazz != nullptr) << "disableInterfaceTokenHeader requires non-null clazz"; + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "disableInterfaceTokenHeader requires non-null clazz"); clazz->writeHeader = false; } void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz, AIBinder_handleShellCommand handleShellCommand) { - CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz"; + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "setHandleShellCommand requires non-null clazz"); clazz->handleShellCommand = handleShellCommand; } const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) { - CHECK(clazz != nullptr) << "getDescriptor requires non-null clazz"; + LOG_ALWAYS_FATAL_IF(clazz == nullptr, "getDescriptor requires non-null clazz"); return clazz->getInterfaceDescriptorUtf8(); } @@ -405,8 +402,8 @@ AIBinder_DeathRecipient::TransferDeathRecipient::~TransferDeathRecipient() { } void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) { - CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get() - << " (" << mWho.get_refs() << ")"; + LOG_ALWAYS_FATAL_IF(who != mWho, "%p (%p) vs %p (%p)", who.unsafe_get(), who.get_refs(), + mWho.unsafe_get(), mWho.get_refs()); mOnDied(mCookie); @@ -417,7 +414,7 @@ void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinde if (recipient != nullptr && strongWho != nullptr) { status_t result = recipient->unlinkToDeath(strongWho, mCookie); if (result != ::android::DEAD_OBJECT) { - LOG(WARNING) << "Unlinking to dead binder resulted in: " << result; + ALOGW("Unlinking to dead binder resulted in: %d", result); } } @@ -426,7 +423,7 @@ void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinde AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied) : mOnDied(onDied), mOnUnlinked(nullptr) { - CHECK(onDied != nullptr); + LOG_ALWAYS_FATAL_IF(onDied == nullptr, "onDied == nullptr"); } void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() { @@ -438,7 +435,7 @@ void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() { } binder_status_t AIBinder_DeathRecipient::linkToDeath(const sp<IBinder>& binder, void* cookie) { - CHECK(binder != nullptr); + LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr"); std::lock_guard<std::mutex> l(mDeathRecipientsMutex); @@ -459,7 +456,7 @@ binder_status_t AIBinder_DeathRecipient::linkToDeath(const sp<IBinder>& binder, } binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder, void* cookie) { - CHECK(binder != nullptr); + LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr"); std::lock_guard<std::mutex> l(mDeathRecipientsMutex); @@ -471,9 +468,8 @@ binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/); if (status != ::android::OK) { - LOG(ERROR) << __func__ - << ": removed reference to death recipient but unlink failed: " - << statusToString(status); + ALOGE("%s: removed reference to death recipient but unlink failed: %s", __func__, + statusToString(status).c_str()); } return PruneStatusT(status); } @@ -490,7 +486,7 @@ void AIBinder_DeathRecipient::setOnUnlinked(AIBinder_DeathRecipient_onBinderUnli AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) { if (clazz == nullptr) { - LOG(ERROR) << __func__ << ": Must provide class to construct local binder."; + ALOGE("%s: Must provide class to construct local binder.", __func__); return nullptr; } @@ -554,8 +550,7 @@ binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint3 binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" - << recipient << ")"; + ALOGE("%s: Must provide binder (%p) and recipient (%p)", __func__, binder, recipient); return STATUS_UNEXPECTED_NULL; } @@ -566,8 +561,7 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" - << recipient << ")"; + ALOGE("%s: Must provide binder (%p) and recipient (%p)", __func__, binder, recipient); return STATUS_UNEXPECTED_NULL; } @@ -596,7 +590,7 @@ void AIBinder_incStrong(AIBinder* binder) { } void AIBinder_decStrong(AIBinder* binder) { if (binder == nullptr) { - LOG(ERROR) << __func__ << ": on null binder"; + ALOGE("%s: on null binder", __func__); return; } @@ -604,7 +598,7 @@ void AIBinder_decStrong(AIBinder* binder) { } int32_t AIBinder_debugGetRefCount(AIBinder* binder) { if (binder == nullptr) { - LOG(ERROR) << __func__ << ": on null binder"; + ALOGE("%s: on null binder", __func__); return -1; } @@ -642,15 +636,14 @@ void* AIBinder_getUserData(AIBinder* binder) { binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { if (binder == nullptr || in == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder - << ") and in (" << in << ")."; + ALOGE("%s: requires non-null parameters binder (%p) and in (%p).", __func__, binder, in); return STATUS_UNEXPECTED_NULL; } const AIBinder_Class* clazz = binder->getClass(); if (clazz == nullptr) { - LOG(ERROR) << __func__ - << ": Class must be defined for a remote binder transaction. See " - "AIBinder_associateClass."; + ALOGE("%s: Class must be defined for a remote binder transaction. See " + "AIBinder_associateClass.", + __func__); return STATUS_INVALID_OPERATION; } @@ -683,7 +676,7 @@ static void DestroyParcel(AParcel** parcel) { binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in, AParcel** out, binder_flags_t flags) { if (in == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null in parameter"; + ALOGE("%s: requires non-null in parameter", __func__); return STATUS_UNEXPECTED_NULL; } @@ -693,27 +686,26 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa AutoParcelDestroyer forIn(in, DestroyParcel); if (!isUserCommand(code)) { - LOG(ERROR) << __func__ - << ": Only user-defined transactions can be made from the NDK, but requested: " - << code; + ALOGE("%s: Only user-defined transactions can be made from the NDK, but requested: %d", + __func__, code); return STATUS_UNKNOWN_TRANSACTION; } constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY | FLAG_CLEAR_BUF; if ((flags & ~kAllFlags) != 0) { - LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags; + ALOGE("%s: Unrecognized flags sent: %d", __func__, flags); return STATUS_BAD_VALUE; } if (binder == nullptr || *in == nullptr || out == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in (" - << in << "), and out (" << out << ")."; + ALOGE("%s: requires non-null parameters binder (%p), in (%p), and out (%p).", __func__, + binder, in, out); return STATUS_UNEXPECTED_NULL; } if ((*in)->getBinder() != binder) { - LOG(ERROR) << __func__ << ": parcel is associated with binder object " << binder - << " but called with " << (*in)->getBinder(); + ALOGE("%s: parcel is associated with binder object %p but called with %p", __func__, binder, + (*in)->getBinder()); return STATUS_BAD_VALUE; } @@ -733,7 +725,7 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa AIBinder_DeathRecipient* AIBinder_DeathRecipient_new( AIBinder_DeathRecipient_onBinderDied onBinderDied) { if (onBinderDied == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter."; + ALOGE("%s: requires non-null onBinderDied parameter.", __func__); return nullptr; } auto ret = new AIBinder_DeathRecipient(onBinderDied); @@ -799,9 +791,8 @@ binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) { void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) { ABBinder* localBinder = binder->asABBinder(); - if (localBinder == nullptr) { - LOG(FATAL) << "AIBinder_setRequestingSid must be called on a local binder"; - } + LOG_ALWAYS_FATAL_IF(localBinder == nullptr, + "AIBinder_setRequestingSid must be called on a local binder"); localBinder->setRequestingSid(requestingSid); } @@ -816,9 +807,8 @@ void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) { ABBinder* localBinder = binder->asABBinder(); - if (localBinder == nullptr) { - LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder"; - } + LOG_ALWAYS_FATAL_IF(localBinder == nullptr, + "AIBinder_setInheritRt must be called on a local binder"); localBinder->setInheritRt(inheritRt); } 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 0000000000..f178027b35 --- /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 0000000000..eff81045b0 --- /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 1c5f79f791..0843a8e648 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -161,6 +161,51 @@ LIBBINDER_NDK34 { # introduced=UpsideDownCake 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/parcel.cpp b/libs/binder/ndk/parcel.cpp index 037aa2e120..c15bcf968d 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -14,21 +14,20 @@ * limitations under the License. */ +#include <android-base/unique_fd.h> #include <android/binder_parcel.h> #include <android/binder_parcel_platform.h> -#include "parcel_internal.h" - -#include "ibinder_internal.h" -#include "status_internal.h" - -#include <limits> - -#include <android-base/logging.h> -#include <android-base/unique_fd.h> #include <binder/Parcel.h> #include <binder/ParcelFileDescriptor.h> +#include <inttypes.h> #include <utils/Unicode.h> +#include <limits> + +#include "ibinder_internal.h" +#include "parcel_internal.h" +#include "status_internal.h" + using ::android::IBinder; using ::android::Parcel; using ::android::sp; @@ -52,11 +51,11 @@ static binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArr if (length < -1) return STATUS_BAD_VALUE; if (!isNullArray && length < 0) { - LOG(ERROR) << __func__ << ": non-null array but length is " << length; + ALOGE("non-null array but length is %" PRIi32, length); return STATUS_BAD_VALUE; } if (isNullArray && length > 0) { - LOG(ERROR) << __func__ << ": null buffer cannot be for size " << length << " array."; + ALOGE("null buffer cannot be for size %" PRIi32 " array.", length); return STATUS_BAD_VALUE; } @@ -325,7 +324,7 @@ binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) { if (string == nullptr) { if (length != -1) { - LOG(WARNING) << __func__ << ": null string must be used with length == -1."; + ALOGW("null string must be used with length == -1."); return STATUS_BAD_VALUE; } @@ -334,7 +333,7 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t } if (length < 0) { - LOG(WARNING) << __func__ << ": Negative string length: " << length; + ALOGW("Negative string length: %" PRIi32, length); return STATUS_BAD_VALUE; } @@ -342,7 +341,7 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t const ssize_t len16 = utf8_to_utf16_length(str8, length); if (len16 < 0 || len16 >= std::numeric_limits<int32_t>::max()) { - LOG(WARNING) << __func__ << ": Invalid string length: " << len16; + ALOGW("Invalid string length: %zd", len16); return STATUS_BAD_VALUE; } @@ -383,7 +382,7 @@ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, } if (len8 <= 0 || len8 > std::numeric_limits<int32_t>::max()) { - LOG(WARNING) << __func__ << ": Invalid string length: " << len8; + ALOGW("Invalid string length: %zd", len8); return STATUS_BAD_VALUE; } @@ -391,7 +390,7 @@ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, bool success = allocator(stringData, len8, &str8); if (!success || str8 == nullptr) { - LOG(WARNING) << __func__ << ": AParcel_stringAllocator failed to allocate."; + ALOGW("AParcel_stringAllocator failed to allocate."); return STATUS_NO_MEMORY; } diff --git a/libs/binder/ndk/persistable_bundle.cpp b/libs/binder/ndk/persistable_bundle.cpp new file mode 100644 index 0000000000..404611cbba --- /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 0000000000..279c66fdcc --- /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/ndk/process.cpp b/libs/binder/ndk/process.cpp index 0fea57b0a7..0072ac3d3e 100644 --- a/libs/binder/ndk/process.cpp +++ b/libs/binder/ndk/process.cpp @@ -15,12 +15,10 @@ */ #include <android/binder_process.h> +#include <binder/IPCThreadState.h> #include <mutex> -#include <android-base/logging.h> -#include <binder/IPCThreadState.h> - using ::android::IPCThreadState; using ::android::ProcessState; diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index 29777866e2..3bfdc59ec2 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -15,14 +15,12 @@ */ #include <android/binder_manager.h> +#include <binder/IServiceManager.h> +#include <binder/LazyServiceRegistrar.h> #include "ibinder_internal.h" #include "status_internal.h" -#include <android-base/logging.h> -#include <binder/IServiceManager.h> -#include <binder/LazyServiceRegistrar.h> - using ::android::defaultServiceManager; using ::android::IBinder; using ::android::IServiceManager; @@ -115,7 +113,8 @@ struct AServiceManager_NotificationRegistration std::lock_guard<std::mutex> l(m); if (onRegister == nullptr) return; - CHECK_EQ(String8(smInstance), instance); + LOG_ALWAYS_FATAL_IF(String8(smInstance) != instance, "onServiceRegistration: %s != %s", + String8(smInstance).c_str(), instance); sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder); AIBinder_incStrong(ret.get()); @@ -135,8 +134,8 @@ __attribute__((warn_unused_result)) AServiceManager_NotificationRegistration* AServiceManager_registerForServiceNotifications(const char* instance, AServiceManager_onRegister onRegister, void* cookie) { - CHECK_NE(instance, nullptr); - CHECK_NE(onRegister, nullptr) << instance; + LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr"); + LOG_ALWAYS_FATAL_IF(onRegister == nullptr, "onRegister == nullptr for %s", instance); // cookie can be nullptr auto cb = sp<AServiceManager_NotificationRegistration>::make(); @@ -146,8 +145,8 @@ AServiceManager_registerForServiceNotifications(const char* instance, sp<IServiceManager> sm = defaultServiceManager(); if (status_t res = sm->registerForNotifications(String16(instance), cb); res != STATUS_OK) { - LOG(ERROR) << "Failed to register for service notifications for " << instance << ": " - << statusToString(res); + ALOGE("Failed to register for service notifications for %s: %s", instance, + statusToString(res).c_str()); return nullptr; } @@ -157,7 +156,7 @@ AServiceManager_registerForServiceNotifications(const char* instance, void AServiceManager_NotificationRegistration_delete( AServiceManager_NotificationRegistration* notification) { - CHECK_NE(notification, nullptr); + LOG_ALWAYS_FATAL_IF(notification == nullptr, "notification == nullptr"); notification->clear(); notification->decStrong(nullptr); } @@ -172,9 +171,9 @@ bool AServiceManager_isDeclared(const char* instance) { } void AServiceManager_forEachDeclaredInstance(const char* interface, void* context, void (*callback)(const char*, void*)) { - CHECK(interface != nullptr); + LOG_ALWAYS_FATAL_IF(interface == nullptr, "interface == nullptr"); // context may be nullptr - CHECK(callback != nullptr); + LOG_ALWAYS_FATAL_IF(callback == nullptr, "callback == nullptr"); sp<IServiceManager> sm = defaultServiceManager(); for (const String16& instance : sm->getDeclaredInstances(String16(interface))) { @@ -191,9 +190,9 @@ bool AServiceManager_isUpdatableViaApex(const char* instance) { } void AServiceManager_getUpdatableApexName(const char* instance, void* context, void (*callback)(const char*, void*)) { - CHECK_NE(instance, nullptr); + LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr"); // context may be nullptr - CHECK_NE(callback, nullptr); + LOG_ALWAYS_FATAL_IF(callback == nullptr, "callback == nullptr"); sp<IServiceManager> sm = defaultServiceManager(); std::optional<String16> updatableViaApex = sm->updatableViaApex(String16(instance)); diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp index 8ed91a5314..3aac3c0545 100644 --- a/libs/binder/ndk/status.cpp +++ b/libs/binder/ndk/status.cpp @@ -17,8 +17,6 @@ #include <android/binder_status.h> #include "status_internal.h" -#include <android-base/logging.h> - using ::android::status_t; using ::android::statusToString; using ::android::binder::Status; @@ -127,8 +125,8 @@ binder_status_t PruneStatusT(status_t status) { return STATUS_UNKNOWN_ERROR; default: - LOG(WARNING) << __func__ << ": Unknown status_t (" << statusToString(status) - << ") pruned into STATUS_UNKNOWN_ERROR"; + ALOGW("%s: Unknown status_t (%s) pruned into STATUS_UNKNOWN_ERROR", __func__, + statusToString(status).c_str()); return STATUS_UNKNOWN_ERROR; } } @@ -159,8 +157,8 @@ binder_exception_t PruneException(int32_t exception) { return EX_TRANSACTION_FAILED; default: - LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception - << ") pruned into EX_TRANSACTION_FAILED"; + ALOGW("%s: Unknown binder exception (%d) pruned into EX_TRANSACTION_FAILED", __func__, + exception); return EX_TRANSACTION_FAILED; } } diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index a08cb7ab39..6d122c5388 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -21,12 +21,13 @@ use crate::parcel::{BorrowedParcel, Parcel}; 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; use std::ffi::{c_void, CStr, CString}; use std::fmt; -use std::fs::File; +use std::io::Write; use std::marker::PhantomData; use std::ops::Deref; use std::os::raw::c_char; @@ -51,7 +52,7 @@ pub type TransactionFlags = u32; /// 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.") @@ -61,11 +62,13 @@ pub trait Interface: Send + Sync { /// /// This handler is a no-op by default and should be implemented for each /// Binder service struct that wishes to respond to dump transactions. - fn dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> { + fn dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> { Ok(()) } } +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 @@ impl TryFrom<i32> for Stability { /// 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 @@ -162,7 +165,7 @@ pub trait Remotable: Send + Sync { /// Handle a request to invoke the dump transaction on this /// object. - fn on_dump(&self, file: &File, args: &[&CStr]) -> Result<()>; + fn on_dump(&self, file: &mut dyn Write, args: &[&CStr]) -> Result<()>; /// Retrieve the class of this remote object. /// @@ -893,6 +896,23 @@ macro_rules! declare_binder_interface { $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 { @@ -914,8 +934,8 @@ macro_rules! declare_binder_interface { } } - fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> { - self.0.dump(file, args) + fn on_dump(&self, writer: &mut dyn std::io::Write, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> { + self.0.dump(writer, args) } fn get_class() -> $crate::binder_impl::InterfaceClass { @@ -1004,7 +1024,7 @@ macro_rules! declare_binder_interface { $( // 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 @@ macro_rules! declare_binder_interface { } } - 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 @@ macro_rules! declare_binder_interface { } } - 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/native.rs b/libs/binder/rust/src/native.rs index b248f5eb28..b250012801 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -25,6 +25,7 @@ use crate::sys; use std::convert::TryFrom; use std::ffi::{c_void, CStr, CString}; use std::fs::File; +use std::io::Write; use std::mem::ManuallyDrop; use std::ops::Deref; use std::os::raw::c_char; @@ -341,7 +342,7 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { } // Safety: Our caller promised that fd is a file descriptor. We don't // own this file descriptor, so we need to be careful not to drop it. - let file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) }; + let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) }; if args.is_null() && num_args != 0 { return StatusCode::UNEXPECTED_NULL as status_t; @@ -366,7 +367,7 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { // Safety: Our caller promised that the binder has a `T` pointer in its // user data. let binder: &T = unsafe { &*(object as *const T) }; - let res = binder.on_dump(&file, &args); + let res = binder.on_dump(&mut *file, &args); match res { Ok(()) => 0, @@ -569,7 +570,7 @@ impl Remotable for () { Ok(()) } - fn on_dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> { + fn on_dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> { Ok(()) } diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs index 5c688fa71b..6afe5ab42b 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::binder::AsNative; 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 @@ impl DeserializeOption for ParcelFileDescriptor { // 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/integration.rs b/libs/binder/rust/tests/integration.rs index c049b807df..c87fa89756 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -26,7 +26,7 @@ use binder::binder_impl::{ use std::convert::{TryFrom, TryInto}; use std::ffi::CStr; -use std::fs::File; +use std::io::Write; use std::sync::Mutex; /// Name of service runner. @@ -118,7 +118,7 @@ impl TryFrom<u32> for TestTransactionCode { } impl Interface for TestService { - fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> { + fn dump(&self, _writer: &mut dyn Write, args: &[&CStr]) -> Result<(), StatusCode> { let mut dump_args = self.dump_args.lock().unwrap(); dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned())); Ok(()) diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs index 37f182eb08..fbedfee956 100644 --- a/libs/binder/rust/tests/ndk_rust_interop.rs +++ b/libs/binder/rust/tests/ndk_rust_interop.rs @@ -58,7 +58,7 @@ pub unsafe extern "C" fn rust_call_ndk(service_name: *const c_char) -> c_int { 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/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index 6712c9cece..7e0b59463a 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -16,6 +16,7 @@ #include <android-base/logging.h> #include <binder/Binder.h> +#include <binder/Functional.h> #include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> @@ -28,6 +29,8 @@ #include <functional> #include <vector> +using namespace android::binder::impl; + static android::String8 gEmpty(""); // make sure first allocation from optimization runs struct DestructionAction { @@ -172,6 +175,18 @@ TEST(BinderAllocation, PingTransaction) { a_binder->pingBinder(); } +TEST(BinderAllocation, MakeScopeGuard) { + const auto m = ScopeDisallowMalloc(); + { + auto guard1 = make_scope_guard([] {}); + guard1.release(); + + auto guard2 = make_scope_guard([&guard1, ptr = imaginary_use] { + if (ptr == nullptr) guard1.release(); + }); + } +} + TEST(BinderAllocation, InterfaceDescriptorTransaction) { sp<IBinder> a_binder = GetRemoteBinder(); diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp index 0075688ed3..0ae536c6bc 100644 --- a/libs/binder/tests/binderHostDeviceTest.cpp +++ b/libs/binder/tests/binderHostDeviceTest.cpp @@ -75,7 +75,7 @@ class HostDeviceTest : public ::testing::Test { public: void SetUp() override { auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr); - ASSERT_THAT(debuggableResult, Ok()); + ASSERT_TRUE(debuggableResult.has_value()); ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult; auto debuggableBool = ParseBool(Trim(debuggableResult->stdoutStr)); ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdoutStr); @@ -84,7 +84,7 @@ public: } auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr); - ASSERT_THAT(lsResult, Ok()); + ASSERT_TRUE(lsResult.has_value()); if (lsResult->exitCode != 0) { GTEST_SKIP() << "b/182914638: until feature is fully enabled, skip test on devices " "without servicedispatcher"; @@ -95,7 +95,7 @@ public: auto service = execute({"adb", "shell", kServiceBinary, kServiceName, kDescriptor}, &CommandResult::stdoutEndsWithNewLine); - ASSERT_THAT(service, Ok()); + ASSERT_TRUE(service.has_value()); ASSERT_EQ(std::nullopt, service->exitCode) << *service; mService = std::move(*service); } diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 341e9ce5ec..f3969f1a01 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -29,17 +29,17 @@ #include <android-base/properties.h> #include <android-base/result-gmock.h> -#include <android-base/result.h> -#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <binder/Binder.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/IBinder.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/RpcServer.h> #include <binder/RpcSession.h> +#include <utils/Flattenable.h> #include <linux/sched.h> #include <sys/epoll.h> @@ -52,6 +52,7 @@ #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) using namespace android; +using namespace android::binder::impl; using namespace std::string_literals; using namespace std::chrono_literals; using android::base::testing::HasValue; @@ -1325,7 +1326,7 @@ TEST_F(BinderLibTest, TooManyFdsFlattenable) { ASSERT_EQ(0, ret); // Restore the original file limits when the test finishes - base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); + auto guardUnguard = make_scope_guard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); rlimit testNofile = {1024, 1024}; ret = setrlimit(RLIMIT_NOFILE, &testNofile); diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index bc34d4c36f..624edba9cd 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" @@ -86,8 +87,8 @@ public: android::base::borrowed_fd /* readEnd */)>& f) { android::base::unique_fd childWriteEnd; android::base::unique_fd childReadEnd; - CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno); - CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno); + if (!android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) PLOGF("child write pipe failed"); + if (!android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) PLOGF("child read pipe failed"); if (0 == (mPid = fork())) { // racey: assume parent doesn't crash before this is set prctl(PR_SET_PDEATHSIG, SIGHUP); @@ -145,8 +146,10 @@ static base::unique_fd initUnixSocket(std::string addr) { auto socket_addr = UnixSocketAddress(addr.c_str()); base::unique_fd fd( TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX))); - CHECK(fd.ok()); - CHECK_EQ(0, TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize()))); + if (!fd.ok()) PLOGF("initUnixSocket failed to create socket"); + if (0 != TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize()))) { + PLOGF("initUnixSocket failed to bind"); + } return fd; } @@ -204,14 +207,12 @@ public: static base::unique_fd connectTo(const RpcSocketAddress& addr) { base::unique_fd serverFd( TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); - int savedErrno = errno; - CHECK(serverFd.ok()) << "Could not create socket " << addr.toString() << ": " - << strerror(savedErrno); + if (!serverFd.ok()) { + PLOGF("Could not create socket %s", addr.toString().c_str()); + } if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) { - int savedErrno = errno; - LOG(FATAL) << "Could not connect to socket " << addr.toString() << ": " - << strerror(savedErrno); + PLOGF("Could not connect to socket %s", addr.toString().c_str()); } return serverFd; } @@ -220,8 +221,7 @@ static base::unique_fd connectTo(const RpcSocketAddress& addr) { static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) { base::unique_fd sockClient, sockServer; if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) { - int savedErrno = errno; - LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno); + PLOGF("Failed socketpair()"); } int zero = 0; @@ -230,8 +230,7 @@ static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) fds.emplace_back(std::move(sockServer)); if (binder::os::sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) { - int savedErrno = errno; - LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno); + PLOGF("Failed sendMessageOnSocket"); } return std::move(sockClient); } @@ -245,10 +244,12 @@ std::unique_ptr<RpcTransportCtxFactory> BinderRpc::newFactory(RpcSecurity rpcSec // threads. std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( const BinderRpcOptions& options) { - CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server"; + LOG_ALWAYS_FATAL_IF(options.numSessions < 1, "Must have at least one session to a server"); if (options.numIncomingConnectionsBySession.size() != 0) { - CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions); + LOG_ALWAYS_FATAL_IF(options.numIncomingConnectionsBySession.size() != options.numSessions, + "%s: %zu != %zu", __func__, + options.numIncomingConnectionsBySession.size(), options.numSessions); } SocketType socketType = GetParam().type; @@ -273,8 +274,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec. // This is because we cannot pass ParcelFileDescriptor over a pipe. if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &socketFd)) { - int savedErrno = errno; - LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno); + PLOGF("Failed socketpair()"); } } @@ -287,8 +287,10 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( auto writeFd = std::to_string(writeEnd.get()); auto readFd = std::to_string(readEnd.get()); - execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(), - NULL); + auto status = execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), + readFd.c_str(), NULL); + PLOGF("execl('%s', _, %s, %s) should not return at all, but it returned %d", + servicePath.c_str(), writeFd.c_str(), readFd.c_str(), status); })); BinderRpcTestServerConfig serverConfig; @@ -333,16 +335,16 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( } writeToFd(ret->host.writeEnd(), clientInfo); - CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max()); + LOG_ALWAYS_FATAL_IF(serverInfo.port > std::numeric_limits<unsigned int>::max()); if (socketType == SocketType::INET) { - CHECK_NE(0, serverInfo.port); + LOG_ALWAYS_FATAL_IF(0 == serverInfo.port); } if (rpcSecurity == RpcSecurity::TLS) { const auto& serverCert = serverInfo.cert.data; - CHECK_EQ(OK, - certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, - serverCert)); + LOG_ALWAYS_FATAL_IF( + OK != + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert)); } } @@ -355,7 +357,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( ? options.numIncomingConnectionsBySession.at(i) : 0; - CHECK(session->setProtocolVersion(clientVersion)); + LOG_ALWAYS_FATAL_IF(!session->setProtocolVersion(clientVersion)); session->setMaxIncomingThreads(numIncoming); session->setMaxOutgoingConnections(options.numOutgoingConnections); session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); @@ -407,7 +409,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( ret->sessions.clear(); break; } - CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status); + LOG_ALWAYS_FATAL_IF(status != OK, "Could not connect: %s", statusToString(status).c_str()); ret->sessions.push_back({session, session->getRootObject()}); } return ret; @@ -588,12 +590,12 @@ TEST_P(BinderRpc, OnewayCallQueueingWithFds) { android::os::ParcelFileDescriptor fdA; EXPECT_OK(proc.rootIface->blockingRecvFd(&fdA)); std::string result; - CHECK(android::base::ReadFdToString(fdA.get(), &result)); + ASSERT_TRUE(android::base::ReadFdToString(fdA.get(), &result)); EXPECT_EQ(result, "a"); android::os::ParcelFileDescriptor fdB; EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB)); - CHECK(android::base::ReadFdToString(fdB.get(), &result)); + ASSERT_TRUE(android::base::ReadFdToString(fdB.get(), &result)); EXPECT_EQ(result, "b"); saturateThreadPool(kNumServerThreads, proc.rootIface); @@ -950,8 +952,8 @@ TEST_P(BinderRpc, ReceiveFile) { ASSERT_TRUE(status.isOk()) << status; std::string result; - CHECK(android::base::ReadFdToString(out.get(), &result)); - EXPECT_EQ(result, "hello"); + ASSERT_TRUE(android::base::ReadFdToString(out.get(), &result)); + ASSERT_EQ(result, "hello"); } TEST_P(BinderRpc, SendFiles) { @@ -980,7 +982,7 @@ TEST_P(BinderRpc, SendFiles) { ASSERT_TRUE(status.isOk()) << status; std::string result; - CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_TRUE(android::base::ReadFdToString(out.get(), &result)); EXPECT_EQ(result, "123abcd"); } @@ -1005,7 +1007,7 @@ TEST_P(BinderRpc, SendMaxFiles) { ASSERT_TRUE(status.isOk()) << status; std::string result; - CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_TRUE(android::base::ReadFdToString(out.get(), &result)); EXPECT_EQ(result, std::string(253, 'a')); } @@ -1157,7 +1159,7 @@ bool testSupportVsockLoopback() { return false; } - LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno)); + LOG_ALWAYS_FATAL_IF(!serverFd.ok(), "Could not create socket: %s", strerror(errno)); sockaddr_vm serverAddr{ .svm_family = AF_VSOCK, @@ -1179,7 +1181,7 @@ bool testSupportVsockLoopback() { // and they return ETIMEDOUT after that. android::base::unique_fd connectFd( TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); - LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort, + LOG_ALWAYS_FATAL_IF(!connectFd.ok(), "Could not create socket for port %u: %s", vsockPort, strerror(errno)); bool success = false; @@ -1197,7 +1199,7 @@ bool testSupportVsockLoopback() { {.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) { @@ -1358,7 +1360,11 @@ private: }; TEST(BinderRpc, Java) { -#if !defined(__ANDROID__) + bool expectDebuggable = false; +#if defined(__ANDROID__) + expectDebuggable = android::base::GetBoolProperty("ro.debuggable", false) && + android::base::GetProperty("ro.build.type", "") != "user"; +#else GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on" "createRpcDelegateServiceManager() with a device attached, such test belongs " "to binderHostDeviceTest. Hence, just disable this test on host."; @@ -1386,8 +1392,7 @@ TEST(BinderRpc, Java) { auto keepAlive = sp<BBinder>::make(); auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive); - if (!android::base::GetBoolProperty("ro.debuggable", false) || - android::base::GetProperty("ro.build.type", "") == "user") { + if (!expectDebuggable) { ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus) << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable or user " "builds, but get " @@ -1592,12 +1597,10 @@ public: iovec iov{&buf, sizeof(buf)}; if (binder::os::receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) { - int savedErrno = errno; - LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno); - } - if (fds.size() != 1) { - LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size(); + PLOGF("Failed receiveMessage"); } + LOG_ALWAYS_FATAL_IF(fds.size() != 1, "Expected one FD from receiveMessage(), got %zu", + fds.size()); return std::move(std::get<base::unique_fd>(fds[0])); } @@ -2082,7 +2085,7 @@ INSTANTIATE_TEST_CASE_P( int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter); + __android_log_set_logger(__android_log_stderr_logger); return RUN_ALL_TESTS(); } diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index c3070ddd14..b2b63e4bf3 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -36,10 +36,12 @@ #include <string> #include <vector> +#ifdef __ANDROID__ +#include <android-base/properties.h> +#endif + #ifndef __TRUSTY__ #include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/properties.h> #include <android/binder_auto_utils.h> #include <android/binder_libbinder.h> #include <binder/ProcessState.h> @@ -156,21 +158,21 @@ struct BinderRpcOptions { #ifndef __TRUSTY__ static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { uint64_t length = str.length(); - CHECK(android::base::WriteFully(fd, &length, sizeof(length))); - CHECK(android::base::WriteFully(fd, str.data(), str.length())); + LOG_ALWAYS_FATAL_IF(!android::base::WriteFully(fd, &length, sizeof(length))); + LOG_ALWAYS_FATAL_IF(!android::base::WriteFully(fd, str.data(), str.length())); } static inline std::string readString(android::base::borrowed_fd fd) { uint64_t length; - CHECK(android::base::ReadFully(fd, &length, sizeof(length))); + LOG_ALWAYS_FATAL_IF(!android::base::ReadFully(fd, &length, sizeof(length))); std::string ret(length, '\0'); - CHECK(android::base::ReadFully(fd, ret.data(), length)); + LOG_ALWAYS_FATAL_IF(!android::base::ReadFully(fd, ret.data(), length)); return ret; } static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { Parcel parcel; - CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); + LOG_ALWAYS_FATAL_IF(OK != parcelable.writeToParcel(&parcel)); writeString(fd, std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize())); } @@ -178,9 +180,10 @@ template <typename T> static inline T readFromFd(android::base::borrowed_fd fd) { std::string data = readString(fd); Parcel parcel; - CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); + LOG_ALWAYS_FATAL_IF(OK != + parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); T object; - CHECK_EQ(OK, object.readFromParcel(&parcel)); + LOG_ALWAYS_FATAL_IF(OK != object.readFromParcel(&parcel)); return object; } @@ -207,7 +210,7 @@ static inline std::unique_ptr<RpcTransportCtxFactory> newTlsFactory( // Create an FD that returns `contents` when read. static inline base::unique_fd mockFileDescriptor(std::string contents) { android::base::unique_fd readFd, writeFd; - CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno); + LOG_ALWAYS_FATAL_IF(!android::base::Pipe(&readFd, &writeFd), "%s", strerror(errno)); RpcMaybeThread([writeFd = std::move(writeFd), contents = std::move(contents)]() { signal(SIGPIPE, SIG_IGN); // ignore possible SIGPIPE from the write if (!WriteStringToFd(contents, writeFd)) { diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp index 7435f308d1..5025bd617f 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -65,7 +65,7 @@ public: std::string acc; for (const auto& file : files) { std::string result; - CHECK(android::base::ReadFdToString(file.get(), &result)); + LOG_ALWAYS_FATAL_IF(!android::base::ReadFdToString(file.get(), &result)); acc.append(result); } out->reset(mockFileDescriptor(acc)); @@ -98,7 +98,7 @@ public: }; int main(int argc, char* argv[]) { - android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter); + __android_log_set_logger(__android_log_stderr_logger); LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc); base::unique_fd writeEnd(atoi(argv[1])); @@ -118,7 +118,7 @@ int main(int argc, char* argv[]) { auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); sp<RpcServer> server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier)); - CHECK(server->setProtocolVersion(serverConfig.serverVersion)); + LOG_ALWAYS_FATAL_IF(!server->setProtocolVersion(serverConfig.serverVersion)); server->setMaxThreads(serverConfig.numThreads); server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes); @@ -129,22 +129,25 @@ int main(int argc, char* argv[]) { case SocketType::PRECONNECTED: [[fallthrough]]; case SocketType::UNIX: - CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str())) - << serverConfig.addr; + LOG_ALWAYS_FATAL_IF(OK != server->setupUnixDomainServer(serverConfig.addr.c_str()), + "%s", serverConfig.addr.c_str()); break; case SocketType::UNIX_BOOTSTRAP: - CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(socketFd))); + LOG_ALWAYS_FATAL_IF(OK != + server->setupUnixDomainSocketBootstrapServer(std::move(socketFd))); break; case SocketType::UNIX_RAW: - CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd))); + LOG_ALWAYS_FATAL_IF(OK != server->setupRawSocketServer(std::move(socketFd))); break; case SocketType::VSOCK: - CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort)) - << "Need `sudo modprobe vsock_loopback`?"; + LOG_ALWAYS_FATAL_IF(OK != + server->setupVsockServer(VMADDR_CID_LOCAL, + serverConfig.vsockPort), + "Need `sudo modprobe vsock_loopback`?"); break; case SocketType::INET: { - CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); - CHECK_NE(0, outPort); + LOG_ALWAYS_FATAL_IF(OK != server->setupInetServer(kLocalInetAddress, 0, &outPort)); + LOG_ALWAYS_FATAL_IF(0 == outPort); break; } default: @@ -159,21 +162,21 @@ int main(int argc, char* argv[]) { if (rpcSecurity == RpcSecurity::TLS) { for (const auto& clientCert : clientInfo.certs) { - CHECK_EQ(OK, - certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, - clientCert.data)); + LOG_ALWAYS_FATAL_IF(OK != + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, + clientCert.data)); } } server->setPerSessionRootObject([&](wp<RpcSession> session, const void* addrPtr, size_t len) { { sp<RpcSession> spSession = session.promote(); - CHECK_NE(nullptr, spSession.get()); + LOG_ALWAYS_FATAL_IF(nullptr == spSession.get()); } // UNIX sockets with abstract addresses return // sizeof(sa_family_t)==2 in addrlen - CHECK_GE(len, sizeof(sa_family_t)); + LOG_ALWAYS_FATAL_IF(len < sizeof(sa_family_t)); const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr); sp<MyBinderRpcTestAndroid> service = sp<MyBinderRpcTestAndroid>::make(); switch (addr->sa_family) { @@ -181,15 +184,15 @@ int main(int argc, char* argv[]) { // nothing to save break; case AF_VSOCK: - CHECK_EQ(len, sizeof(sockaddr_vm)); + LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_vm)); service->port = reinterpret_cast<const sockaddr_vm*>(addr)->svm_port; break; case AF_INET: - CHECK_EQ(len, sizeof(sockaddr_in)); + LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in)); service->port = ntohs(reinterpret_cast<const sockaddr_in*>(addr)->sin_port); break; case AF_INET6: - CHECK_EQ(len, sizeof(sockaddr_in)); + LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in)); service->port = ntohs(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port); break; default: diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index 7ec7c99f06..e59dc82b2b 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -15,7 +15,6 @@ */ #include <android-base/logging.h> -#include <android-base/macros.h> #include <android-base/properties.h> #include <android-base/strings.h> #include <binder/Parcel.h> diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 1c13866626..cbbbe74bb5 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -28,6 +28,7 @@ #include <gtest/gtest.h> #pragma clang diagnostic pop +#include <utils/Flattenable.h> #include <utils/LightRefBase.h> #include <utils/NativeHandle.h> diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index 25e286ce0c..6301c74842 100644 --- a/libs/binder/tests/binderUtilsHostTest.cpp +++ b/libs/binder/tests/binderUtilsHostTest.cpp @@ -32,7 +32,7 @@ namespace android { TEST(UtilsHost, ExecuteImmediately) { auto result = execute({"echo", "foo"}, nullptr); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_THAT(result->exitCode, Optional(EX_OK)); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -58,7 +58,7 @@ TEST(UtilsHost, ExecuteLongRunning) { EXPECT_GE(elapsedMs, 1000); EXPECT_LT(elapsedMs, 2000); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -83,7 +83,7 @@ TEST(UtilsHost, ExecuteLongRunning2) { EXPECT_GE(elapsedMs, 4000); EXPECT_LT(elapsedMs, 6000); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -104,7 +104,7 @@ TEST(UtilsHost, KillWithSigKill) { return false; }); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_THAT(result->signal, Optional(SIGKILL)); } diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 383795eff5..fe79f8ebe9 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -32,7 +32,11 @@ cc_fuzz { host_supported: true, fuzz_config: { - cc: ["smoreland@google.com"], + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + use_for_presubmit: true, }, srcs: [ diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 416ffad466..ffeca2d13e 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -25,6 +25,7 @@ #include <binder/ParcelableHolder.h> #include <binder/PersistableBundle.h> #include <binder/Status.h> +#include <utils/Flattenable.h> #include "../../Utils.h" diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp index f3006cdeee..070618294f 100644 --- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <android-base/macros.h> #include <binder/RecordedTransaction.h> #include <filesystem> @@ -36,7 +35,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { intermediateFile = std::tmpfile(); android::base::unique_fd fdForWriting(dup(fileno(intermediateFile))); - auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting); + auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting); std::fclose(intermediateFile); } diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp index 33a653eb33..9289f6ac90 100644 --- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <android-base/macros.h> #include <binder/RecordedTransaction.h> #include <fuzzbinder/random_parcel.h> #include <filesystem> @@ -55,7 +54,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (transaction.has_value()) { std::FILE* intermediateFile = std::tmpfile(); android::base::unique_fd fdForWriting(dup(fileno(intermediateFile))); - auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting); + auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting); std::fclose(intermediateFile); } diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp index 43e06e013d..0d18b0b94d 100644 --- a/libs/binder/trusty/OS.cpp +++ b/libs/binder/trusty/OS.cpp @@ -26,13 +26,11 @@ #include "../OS.h" #include "TrustyStatus.h" -using android::base::Result; - namespace android::binder::os { -Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) { +status_t setNonBlocking(android::base::borrowed_fd /*fd*/) { // Trusty IPC syscalls are all non-blocking by default. - return {}; + return OK; } status_t getRandomBytes(uint8_t* data, size_t size) { diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp index 692f82d6cd..6bb45e2e11 100644 --- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -29,6 +29,8 @@ namespace android { +using namespace android::binder::impl; + // RpcTransport for Trusty. class RpcTransportTipcTrusty : public RpcTransport { public: @@ -45,7 +47,7 @@ public: status_t interruptableWriteFully( FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/, + const std::optional<SmallFunction<status_t()>>& /*altPoll*/, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { if (niovs < 0) { @@ -115,7 +117,7 @@ public: status_t interruptableReadFully( FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/, + const std::optional<SmallFunction<status_t()>>& /*altPoll*/, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { if (niovs < 0) { return BAD_VALUE; diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json index 1cefac5a2b..6e20b8a7ad 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/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h index 8924b3679f..aa476f946a 100644 --- a/libs/binder/trusty/include/binder/RpcServerTrusty.h +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -17,7 +17,6 @@ #pragma once #include <android-base/expected.h> -#include <android-base/macros.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/RpcServer.h> @@ -83,7 +82,8 @@ private: // Both this class and RpcServer have multiple non-copyable fields, // including mPortAcl below which can't be copied because mUuidPtrs // holds pointers into it - DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty); + RpcServerTrusty(const RpcServerTrusty&) = delete; + void operator=(const RpcServerTrusty&) = delete; friend sp<RpcServerTrusty>; explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk index 1f05ef757a..d2b37aa8f6 100644 --- a/libs/binder/trusty/kernel/rules.mk +++ b/libs/binder/trusty/kernel/rules.mk @@ -24,7 +24,6 @@ LIBUTILS_DIR := system/core/libutils FMTLIB_DIR := external/fmtlib MODULE_SRCS := \ - $(LOCAL_DIR)/../logging.cpp \ $(LOCAL_DIR)/../TrustyStatus.cpp \ $(LIBBINDER_DIR)/Binder.cpp \ $(LIBBINDER_DIR)/BpBinder.cpp \ diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp deleted file mode 100644 index b4243af891..0000000000 --- a/libs/binder/trusty/logging.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2022 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. - */ - -#define TLOG_TAG "libbinder" - -#include "android-base/logging.h" - -#include <trusty_log.h> -#include <iostream> -#include <string> - -#include <android-base/macros.h> -#include <android-base/strings.h> - -namespace android { -namespace base { - -static const char* GetFileBasename(const char* file) { - const char* last_slash = strrchr(file, '/'); - if (last_slash != nullptr) { - return last_slash + 1; - } - return file; -} - -// This splits the message up line by line, by calling log_function with a pointer to the start of -// each line and the size up to the newline character. It sends size = -1 for the final line. -template <typename F, typename... Args> -static void SplitByLines(const char* msg, const F& log_function, Args&&... args) { - const char* newline; - while ((newline = strchr(msg, '\n')) != nullptr) { - log_function(msg, newline - msg, args...); - msg = newline + 1; - } - - log_function(msg, -1, args...); -} - -void DefaultAborter(const char* abort_message) { - TLOGC("aborting: %s\n", abort_message); - abort(); -} - -static void TrustyLogLine(const char* msg, int /*length*/, android::base::LogSeverity severity, - const char* tag) { - switch (severity) { - case VERBOSE: - case DEBUG: - TLOGD("%s: %s\n", tag, msg); - break; - case INFO: - TLOGI("%s: %s\n", tag, msg); - break; - case WARNING: - TLOGW("%s: %s\n", tag, msg); - break; - case ERROR: - TLOGE("%s: %s\n", tag, msg); - break; - case FATAL_WITHOUT_ABORT: - case FATAL: - TLOGC("%s: %s\n", tag, msg); - break; - } -} - -void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag, - const char*, unsigned int, const char* full_message) { - SplitByLines(full_message, TrustyLogLine, severity, tag); -} - -// This indirection greatly reduces the stack impact of having lots of -// checks/logging in a function. -class LogMessageData { -public: - LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag, - int error) - : file_(GetFileBasename(file)), - line_number_(line), - severity_(severity), - tag_(tag), - error_(error) {} - - const char* GetFile() const { return file_; } - - unsigned int GetLineNumber() const { return line_number_; } - - LogSeverity GetSeverity() const { return severity_; } - - const char* GetTag() const { return tag_; } - - int GetError() const { return error_; } - - std::ostream& GetBuffer() { return buffer_; } - - std::string ToString() const { return buffer_.str(); } - -private: - std::ostringstream buffer_; - const char* const file_; - const unsigned int line_number_; - const LogSeverity severity_; - const char* const tag_; - const int error_; - - DISALLOW_COPY_AND_ASSIGN(LogMessageData); -}; - -LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, - const char* tag, int error) - : LogMessage(file, line, severity, tag, error) {} - -LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, - int error) - : data_(new LogMessageData(file, line, severity, tag, error)) {} - -LogMessage::~LogMessage() { - // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM. - if (!WOULD_LOG(data_->GetSeverity())) { - return; - } - - // Finish constructing the message. - if (data_->GetError() != -1) { - data_->GetBuffer() << ": " << strerror(data_->GetError()); - } - std::string msg(data_->ToString()); - - LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(), - msg.c_str()); - - // Abort if necessary. - if (data_->GetSeverity() == FATAL) { - DefaultAborter(msg.c_str()); - } -} - -std::ostream& LogMessage::stream() { - return data_->GetBuffer(); -} - -void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag, - const char* message) { - TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message); -} - -bool ShouldLog(LogSeverity /*severity*/, const char* /*tag*/) { - // This is controlled by Trusty's log level. - return true; -} - -} // namespace base -} // namespace android diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk index 2e56cbd21b..9cad556711 100644 --- a/libs/binder/trusty/rules.mk +++ b/libs/binder/trusty/rules.mk @@ -24,7 +24,6 @@ LIBUTILS_DIR := system/core/libutils FMTLIB_DIR := external/fmtlib MODULE_SRCS := \ - $(LOCAL_DIR)/logging.cpp \ $(LOCAL_DIR)/OS.cpp \ $(LOCAL_DIR)/RpcServerTrusty.cpp \ $(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \ diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index ed5d5c1095..394a0002ed 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -575,17 +575,23 @@ android_namespace_t* GraphicsEnv::getAngleNamespace() { return mAngleNamespace; } - if (mAnglePath.empty() && !mShouldUseSystemAngle) { - ALOGV("mAnglePath is empty and not using system ANGLE, abort creating ANGLE namespace"); + // If ANGLE path is not set, it means ANGLE should not be used for this process; + // or if ANGLE path is set and set to use system ANGLE, then a namespace is not needed + // because: + // 1) if the default OpenGL ES driver is already ANGLE, then the loader will skip; + // 2) if the default OpenGL ES driver is native, then there's no symbol conflict; + // 3) if there's no OpenGL ES driver is preloaded, then there's no symbol conflict. + if (mAnglePath.empty() || mShouldUseSystemAngle) { + ALOGV("mAnglePath is empty or use system ANGLE, abort creating ANGLE namespace"); return nullptr; } // Construct the search paths for system ANGLE. const char* const defaultLibraryPaths = #if defined(__LP64__) - "/vendor/lib64/egl:/system/lib64/egl"; + "/vendor/lib64/egl:/system/lib64"; #else - "/vendor/lib/egl:/system/lib/egl"; + "/vendor/lib/egl:/system/lib"; #endif // If the application process will run on top of system ANGLE, construct the namespace diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h index 47607a0ab9..9ebaf16eb4 100644 --- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -104,7 +104,7 @@ public: GL_UPDATED = 2, VULKAN = 3, VULKAN_UPDATED = 4, - ANGLE = 5, + ANGLE = 5, // cover both system ANGLE and ANGLE APK }; enum Stats { diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 8d0331ebb5..f317a2eea0 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -41,6 +41,8 @@ #include <android-base/thread_annotations.h> #include <chrono> +#include <com_android_graphics_libgui_flags.h> + using namespace com::android::graphics::libgui; using namespace std::chrono_literals; @@ -102,12 +104,11 @@ void BLASTBufferItemConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry } } -void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime, - const sp<Fence>& glDoneFence, - const sp<Fence>& presentFence, - const sp<Fence>& prevReleaseFence, - CompositorTiming compositorTiming, - nsecs_t latchTime, nsecs_t dequeueReadyTime) { +void BLASTBufferItemConsumer::updateFrameTimestamps( + uint64_t frameNumber, uint64_t previousFrameNumber, nsecs_t refreshStartTime, + const sp<Fence>& glDoneFence, const sp<Fence>& presentFence, + const sp<Fence>& prevReleaseFence, CompositorTiming compositorTiming, nsecs_t latchTime, + nsecs_t dequeueReadyTime) { Mutex::Autolock lock(mMutex); // if the producer is not connected, don't bother updating, @@ -118,7 +119,15 @@ void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_ std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(prevReleaseFence); mFrameEventHistory.addLatch(frameNumber, latchTime); - mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime)); + if (flags::frametimestamps_previousrelease()) { + if (previousFrameNumber > 0) { + mFrameEventHistory.addRelease(previousFrameNumber, dequeueReadyTime, + std::move(releaseFenceTime)); + } + } else { + mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime)); + } + mFrameEventHistory.addPreComposition(frameNumber, refreshStartTime); mFrameEventHistory.addPostComposition(frameNumber, glDoneFenceTime, presentFenceTime, compositorTiming); @@ -364,6 +373,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence if (stat.latchTime > 0) { mBufferItemConsumer ->updateFrameTimestamps(stat.frameEventStats.frameNumber, + stat.frameEventStats.previousFrameNumber, stat.frameEventStats.refreshStartTime, stat.frameEventStats.gpuCompositionDoneFence, stat.presentFence, stat.previousReleaseFence, diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 5b34ba12c8..b6a47fb4e9 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -318,35 +318,44 @@ status_t BufferQueueConsumer::detachBuffer(int slot) { 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 (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 (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(); + mSlots[slot].mBufferState.detachConsumer(); + mCore->mActiveBuffers.erase(slot); + mCore->mFreeSlots.insert(slot); + mCore->clearBufferSlotLocked(slot); + mCore->mDequeueCondition.notify_all(); + VALIDATE_CONSISTENCY(); + } + if (listener) { + listener->onBufferDetached(slot); + } return NO_ERROR; } diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index 29d64afb24..f5d19aac78 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -25,6 +25,10 @@ #include <gui/LayerState.h> #include <private/gui/ParcelUtils.h> +#include <com_android_graphics_libgui_flags.h> + +using namespace com::android::graphics::libgui; + namespace android { namespace { // Anonymous @@ -49,6 +53,11 @@ status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const { status_t err = output->writeUint64(frameNumber); if (err != NO_ERROR) return err; + if (flags::frametimestamps_previousrelease()) { + err = output->writeUint64(previousFrameNumber); + if (err != NO_ERROR) return err; + } + if (gpuCompositionDoneFence) { err = output->writeBool(true); if (err != NO_ERROR) return err; @@ -79,6 +88,11 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) { status_t err = input->readUint64(&frameNumber); if (err != NO_ERROR) return err; + if (flags::frametimestamps_previousrelease()) { + err = input->readUint64(&previousFrameNumber); + if (err != NO_ERROR) return err; + } + bool hasFence = false; err = input->readBool(&hasFence); if (err != NO_ERROR) return err; diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 2eb6bd670d..6a4460b650 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -66,8 +66,9 @@ bool WindowInfo::overlaps(const WindowInfo* other) const { bool WindowInfo::operator==(const WindowInfo& info) const { return info.token == token && info.id == id && info.name == name && info.dispatchingTimeout == dispatchingTimeout && info.frame == frame && - info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor && - info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) && + info.contentSize == contentSize && info.surfaceInset == surfaceInset && + info.globalScaleFactor == globalScaleFactor && info.transform == transform && + info.touchableRegion.hasSameRects(touchableRegion) && info.touchOcclusionMode == touchOcclusionMode && info.ownerPid == ownerPid && info.ownerUid == ownerUid && info.packageName == packageName && info.inputConfig == inputConfig && info.displayId == displayId && @@ -101,6 +102,8 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeInt32( static_cast<std::underlying_type_t<WindowInfo::Type>>(layoutParamsType)) ?: parcel->write(frame) ?: + parcel->writeInt32(contentSize.width) ?: + parcel->writeInt32(contentSize.height) ?: parcel->writeInt32(surfaceInset) ?: parcel->writeFloat(globalScaleFactor) ?: parcel->writeFloat(alpha) ?: @@ -150,6 +153,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { status = parcel->readInt32(&lpFlags) ?: parcel->readInt32(&lpType) ?: parcel->read(frame) ?: + parcel->readInt32(&contentSize.width) ?: + parcel->readInt32(&contentSize.height) ?: parcel->readInt32(&surfaceInset) ?: parcel->readFloat(&globalScaleFactor) ?: parcel->readFloat(&alpha) ?: diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp index 17f4c630ce..2e270b721f 100644 --- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp +++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp @@ -141,8 +141,8 @@ void BufferQueueFuzzer::invokeBlastBufferQueue() { CompositorTiming compTiming; sp<Fence> previousFence = new Fence(memfd_create("pfd", MFD_ALLOW_SEALING)); sp<Fence> gpuFence = new Fence(memfd_create("gfd", MFD_ALLOW_SEALING)); - FrameEventHistoryStats frameStats(frameNumber, gpuFence, compTiming, - mFdp.ConsumeIntegral<int64_t>(), + FrameEventHistoryStats frameStats(frameNumber, mFdp.ConsumeIntegral<uint64_t>(), gpuFence, + compTiming, mFdp.ConsumeIntegral<int64_t>(), mFdp.ConsumeIntegral<int64_t>()); std::vector<SurfaceControlStats> stats; sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 892215ec32..0e1a505c69 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -50,8 +50,8 @@ public: void onDisconnect() override EXCLUDES(mMutex); void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex); - void updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime, - const sp<Fence>& gpuCompositionDoneFence, + void updateFrameTimestamps(uint64_t frameNumber, uint64_t previousFrameNumber, + nsecs_t refreshStartTime, const sp<Fence>& gpuCompositionDoneFence, const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence, CompositorTiming compositorTiming, nsecs_t latchTime, nsecs_t dequeueReadyTime) EXCLUDES(mMutex); diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h index f7ffbb99ea..b15f501518 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h @@ -49,6 +49,12 @@ public: // 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/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h index 364a57b8dd..bc97cd0828 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -95,15 +95,18 @@ public: status_t readFromParcel(const Parcel* input) override; FrameEventHistoryStats() = default; - FrameEventHistoryStats(uint64_t fn, const sp<Fence>& gpuCompFence, CompositorTiming compTiming, + FrameEventHistoryStats(uint64_t frameNumber, uint64_t previousFrameNumber, + const sp<Fence>& gpuCompFence, CompositorTiming compTiming, nsecs_t refreshTime, nsecs_t dequeueReadyTime) - : frameNumber(fn), + : frameNumber(frameNumber), + previousFrameNumber(previousFrameNumber), gpuCompositionDoneFence(gpuCompFence), compositorTiming(compTiming), refreshStartTime(refreshTime), dequeueReadyTime(dequeueReadyTime) {} uint64_t frameNumber; + uint64_t previousFrameNumber; sp<Fence> gpuCompositionDoneFence; CompositorTiming compositorTiming; nsecs_t refreshStartTime; diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index bd2eb7413b..dcc38d7564 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -26,6 +26,7 @@ #include <gui/constants.h> #include <ui/Rect.h> #include <ui/Region.h> +#include <ui/Size.h> #include <ui/Transform.h> #include <utils/RefBase.h> #include <utils/Timers.h> @@ -196,6 +197,9 @@ struct WindowInfo : public Parcelable { /* These values are filled in by SurfaceFlinger. */ Rect frame = Rect::INVALID_RECT; + // The real size of the content, excluding any crop. If no buffer is rendered, this is 0,0 + ui::Size contentSize = ui::Size(0, 0); + /* * SurfaceFlinger consumes this value to shrink the computed frame. This is * different from shrinking the touchable region in that it DOES shift the coordinate diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index a16be786be..b081030c9f 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -8,3 +8,10 @@ flag { is_fixed_read_only: true } +flag { + name: "frametimestamps_previousrelease" + namespace: "core_graphics" + description: "Controls a fence fixup for timestamp apis" + bug: "310927247" + is_fixed_read_only: true +} diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 9893c7146e..ea7078dd05 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -42,9 +42,12 @@ #include <gtest/gtest.h> +#include <com_android_graphics_libgui_flags.h> + using namespace std::chrono_literals; namespace android { +using namespace com::android::graphics::libgui; using Transaction = SurfaceComposerClient::Transaction; using android::hardware::graphics::common::V1_2::BufferUsage; @@ -1581,6 +1584,9 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { nsecs_t postedTimeB = 0; setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true); history.applyDelta(qbOutput.frameTimestamps); + + adapter.waitForCallback(2); + events = history.getFrame(1); ASSERT_NE(nullptr, events); @@ -1590,7 +1596,9 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { ASSERT_GE(events->postedTime, postedTimeA); ASSERT_GE(events->latchTime, postedTimeA); - ASSERT_GE(events->dequeueReadyTime, events->latchTime); + if (flags::frametimestamps_previousrelease()) { + ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING); + } ASSERT_NE(nullptr, events->gpuCompositionDoneFence); ASSERT_NE(nullptr, events->displayPresentFence); ASSERT_NE(nullptr, events->releaseFence); @@ -1602,6 +1610,50 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeB); + // Now do the same as above with a third buffer, so that timings related to + // buffer releases make it back to the first frame. + nsecs_t requestedPresentTimeC = 0; + nsecs_t postedTimeC = 0; + setUpAndQueueBuffer(igbProducer, &requestedPresentTimeC, &postedTimeC, &qbOutput, true); + history.applyDelta(qbOutput.frameTimestamps); + + adapter.waitForCallback(3); + + // Check the first frame... + events = history.getFrame(1); + ASSERT_NE(nullptr, events); + ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeA); + ASSERT_GE(events->latchTime, postedTimeA); + // Now dequeueReadyTime is valid, because the release timings finally + // propaged to queueBuffer() + ASSERT_GE(events->dequeueReadyTime, events->latchTime); + ASSERT_NE(nullptr, events->gpuCompositionDoneFence); + ASSERT_NE(nullptr, events->displayPresentFence); + ASSERT_NE(nullptr, events->releaseFence); + + // ...and the second + events = history.getFrame(2); + ASSERT_NE(nullptr, events); + ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeB); + ASSERT_GE(events->latchTime, postedTimeB); + if (flags::frametimestamps_previousrelease()) { + ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING); + } + ASSERT_NE(nullptr, events->gpuCompositionDoneFence); + ASSERT_NE(nullptr, events->displayPresentFence); + ASSERT_NE(nullptr, events->releaseFence); + + // ...and finally the third! + events = history.getFrame(3); + ASSERT_NE(nullptr, events); + ASSERT_EQ(3, events->frameNumber); + ASSERT_EQ(requestedPresentTimeC, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeC); + // wait for any callbacks that have not been received adapter.waitForCallbacks(); } @@ -1660,6 +1712,8 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { setUpAndQueueBuffer(igbProducer, &requestedPresentTimeC, &postedTimeC, &qbOutput, true); history.applyDelta(qbOutput.frameTimestamps); + adapter.waitForCallback(3); + // frame number, requestedPresentTime, and postTime should not have changed ASSERT_EQ(1, events->frameNumber); ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); @@ -1679,6 +1733,42 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeB); ASSERT_GE(events->latchTime, postedTimeB); + + if (flags::frametimestamps_previousrelease()) { + ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING); + } + ASSERT_NE(nullptr, events->gpuCompositionDoneFence); + ASSERT_NE(nullptr, events->displayPresentFence); + ASSERT_NE(nullptr, events->releaseFence); + + // Queue another buffer to check for timestamps that came late + nsecs_t requestedPresentTimeD = 0; + nsecs_t postedTimeD = 0; + setUpAndQueueBuffer(igbProducer, &requestedPresentTimeD, &postedTimeD, &qbOutput, true); + history.applyDelta(qbOutput.frameTimestamps); + + adapter.waitForCallback(4); + + // frame number, requestedPresentTime, and postTime should not have changed + events = history.getFrame(1); + ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeA); + + // a valid latchtime and pre and post composition info should not be set for the dropped frame + ASSERT_FALSE(events->hasLatchInfo()); + ASSERT_FALSE(events->hasDequeueReadyInfo()); + ASSERT_FALSE(events->hasGpuCompositionDoneInfo()); + ASSERT_FALSE(events->hasDisplayPresentInfo()); + ASSERT_FALSE(events->hasReleaseInfo()); + + // we should also have gotten values for the presented frame + events = history.getFrame(2); + ASSERT_NE(nullptr, events); + ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeB); + ASSERT_GE(events->latchTime, postedTimeB); ASSERT_GE(events->dequeueReadyTime, events->latchTime); ASSERT_NE(nullptr, events->gpuCompositionDoneFence); ASSERT_NE(nullptr, events->displayPresentFence); diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 1410c7dce0..df7739c3fb 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -1187,6 +1187,76 @@ TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) { 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 cf009b7b9a..ec01189059 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -208,21 +208,6 @@ protected: 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 @@ TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) { 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; @@ -1345,7 +1280,7 @@ protected: newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; // HWC2 releases the previous buffer after a new latch just before - // calling postComposition. + // calling onCompositionPresented. if (oldFrame != nullptr) { mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime, std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime)); diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp index f2feaefa13..5eb5d3bff0 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -28,6 +28,7 @@ namespace android { using gui::InputApplicationInfo; using gui::TouchOcclusionMode; using gui::WindowInfo; +using ui::Size; namespace test { @@ -53,6 +54,7 @@ TEST(WindowInfo, Parcelling) { i.layoutParamsType = WindowInfo::Type::INPUT_METHOD; i.dispatchingTimeout = 12s; i.frame = Rect(93, 34, 16, 19); + i.contentSize = Size(10, 40); i.surfaceInset = 17; i.globalScaleFactor = 0.3; i.alpha = 0.7; @@ -83,6 +85,7 @@ TEST(WindowInfo, Parcelling) { ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout); ASSERT_EQ(i.frame, i2.frame); + ASSERT_EQ(i.contentSize, i2.contentSize); ASSERT_EQ(i.surfaceInset, i2.surfaceInset); ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor); ASSERT_EQ(i.alpha, i2.alpha); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 69a4f0aed1..dd8dc8dc9d 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -37,20 +37,20 @@ filegroup { // 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 // library. Bundling this library directly into libinput prevents us from having to add this // library as a shared lib dependency everywhere where libinput is used. - test: true, + mode: "test", shared: { enabled: false, }, @@ -242,7 +242,7 @@ cc_library { ], whole_static_libs: [ - "aconfig_input_flags_c_lib", + "com.android.input.flags-aconfig-cc", "libinput_rust_ffi", ], diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp index 9a459b135c..db7031ab03 100644 --- a/libs/input/VirtualInputDevice.cpp +++ b/libs/input/VirtualInputDevice.cpp @@ -193,6 +193,7 @@ const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = { {AKEYCODE_NUMPAD_ENTER, KEY_KPENTER}, {AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL}, {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA}, + {AKEYCODE_LANGUAGE_SWITCH, KEY_LANGUAGE}, }; VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {} VirtualKeyboard::~VirtualKeyboard() {} diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 978a80f9b3..3672387119 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -48,3 +48,17 @@ flag { description: "Enable additional palm rejection on touchpad while typing" bug: "301055381" } + +flag { + name: "remove_app_switch_drops" + namespace: "input" + description: "Remove the logic of dropping events due to pending app switch" + bug: "284808102" +} + +flag { + name: "disable_reject_touch_on_stylus_hover" + namespace: "input" + description: "Disable touch rejection when the stylus hovers the screen" + bug: "301216095" +} diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp index 538c1d2a42..4246c40f64 100644 --- a/libs/ui/FenceTime.cpp +++ b/libs/ui/FenceTime.cpp @@ -363,9 +363,9 @@ void FenceToFenceTimeMap::signalAllForTest( } void FenceToFenceTimeMap::garbageCollectLocked() { - for (auto& it : mMap) { + for (auto it = mMap.begin(); it != mMap.end();) { // Erase all expired weak pointers from the vector. - auto& vect = it.second; + auto& vect = it->second; vect.erase( std::remove_if(vect.begin(), vect.end(), [](const std::weak_ptr<FenceTime>& ft) { @@ -375,7 +375,9 @@ void FenceToFenceTimeMap::garbageCollectLocked() { // Also erase the map entry if the vector is now empty. if (vect.empty()) { - mMap.erase(it.first); + it = mMap.erase(it); + } else { + it++; } } } diff --git a/opengl/Android.bp b/opengl/Android.bp index b15694bf50..4454f36b67 100644 --- a/opengl/Android.bp +++ b/opengl/Android.bp @@ -72,6 +72,10 @@ cc_library_headers { llndk: { llndk_headers: true, }, + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], } subdirs = [ diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 04e2fffaef..e487cbc54d 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -66,6 +66,8 @@ namespace android { static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl"; static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl"; static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform"; +static const char* ANGLE_SUFFIX_VALUE = "angle"; +static const char* VENDOR_ANGLE_BUILD = "ro.gfx.angle.supported"; static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = { PERSIST_DRIVER_SUFFIX_PROPERTY, @@ -80,6 +82,13 @@ static const char* const VENDOR_LIB_EGL_DIR = "/vendor/lib/egl"; #endif +static const char* const SYSTEM_LIB_DIR = +#if defined(__LP64__) + "/system/lib64"; +#else + "/system/lib"; +#endif + static void* do_dlopen(const char* path, int mode) { ATRACE_CALL(); return dlopen(path, mode); @@ -434,98 +443,110 @@ void Loader::init_api(void* dso, } } -static void* load_system_driver(const char* kind, const char* suffix, const bool exact) { - ATRACE_CALL(); - class MatchFile { - public: - static std::string find(const char* libraryName, const bool exact) { - std::string absolutePath; - if (findLibPath(absolutePath, libraryName, exact)) { - return absolutePath; - } - - // Driver not found. gah. - return std::string(); +static std::string findLibrary(const std::string libraryName, const std::string searchPath, + const bool exact) { + if (exact) { + std::string absolutePath = searchPath + "/" + libraryName + ".so"; + if (!access(absolutePath.c_str(), R_OK)) { + return absolutePath; } - private: - static bool findLibPath(std::string& result, const std::string& pattern, bool exact) { - const std::string vendorLibEglDirString = std::string(VENDOR_LIB_EGL_DIR); - if (exact) { - std::string absolutePath = vendorLibEglDirString + "/" + pattern + ".so"; - if (!access(absolutePath.c_str(), R_OK)) { - result = absolutePath; - return true; - } - return false; - } + return std::string(); + } - DIR* d = opendir(VENDOR_LIB_EGL_DIR); - if (d != nullptr) { - struct dirent* e; - while ((e = readdir(d)) != nullptr) { - if (e->d_type == DT_DIR) { - continue; - } - if (!strcmp(e->d_name, "libGLES_android.so")) { - // always skip the software renderer - continue; - } - if (strstr(e->d_name, pattern.c_str()) == e->d_name) { - if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) { - result = vendorLibEglDirString + "/" + e->d_name; - closedir(d); - return true; - } - } + DIR* d = opendir(searchPath.c_str()); + if (d != nullptr) { + struct dirent* e; + while ((e = readdir(d)) != nullptr) { + if (e->d_type == DT_DIR) { + continue; + } + if (!strcmp(e->d_name, "libGLES_android.so")) { + // always skip the software renderer + continue; + } + if (strstr(e->d_name, libraryName.c_str()) == e->d_name) { + if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) { + std::string result = searchPath + "/" + e->d_name; + closedir(d); + return result; } - closedir(d); } - return false; } - }; + closedir(d); + } + // Driver not found. gah. + return std::string(); +} + +static void* load_system_driver(const char* kind, const char* suffix, const bool exact) { + ATRACE_CALL(); std::string libraryName = std::string("lib") + kind; if (suffix) { libraryName += std::string("_") + suffix; } else if (!exact) { - // Deprecated: we look for files that match - // libGLES_*.so, or: + // Deprecated for devices launching in Android 14 + // Look for files that match + // libGLES_*.so, or, // libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so libraryName += std::string("_"); } - std::string absolutePath = MatchFile::find(libraryName.c_str(), exact); + + void* dso = nullptr; + + const bool AngleInVendor = property_get_bool(VENDOR_ANGLE_BUILD, false); + const bool isSuffixAngle = suffix != nullptr && strcmp(suffix, ANGLE_SUFFIX_VALUE) == 0; + // Only use sphal namespace when system ANGLE binaries are not the default drivers. + const bool useSphalNamespace = !isSuffixAngle || AngleInVendor; + + const std::string absolutePath = + findLibrary(libraryName, useSphalNamespace ? VENDOR_LIB_EGL_DIR : SYSTEM_LIB_PATH, + exact); if (absolutePath.empty()) { // this happens often, we don't want to log an error return nullptr; } - const char* const driver_absolute_path = absolutePath.c_str(); + const char* const driverAbsolutePath = absolutePath.c_str(); + + // Currently the default driver is unlikely to be ANGLE on most devices, + // hence put this first. + if (useSphalNamespace) { + // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to + // the original routine when the namespace does not exist. + // See /system/linkerconfig/contents/namespace for the configuration of the + // sphal namespace. + dso = do_android_load_sphal_library(driverAbsolutePath, RTLD_NOW | RTLD_LOCAL); + } else { + // Try to load drivers from the default namespace. + // See /system/linkerconfig/contents/namespace for the configuration of the + // default namespace. + dso = do_dlopen(driverAbsolutePath, RTLD_NOW | RTLD_LOCAL); + } - // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to - // the original routine when the namespace does not exist. - // See /system/core/rootdir/etc/ld.config.txt for the configuration of the - // sphal namespace. - void* dso = do_android_load_sphal_library(driver_absolute_path, - RTLD_NOW | RTLD_LOCAL); if (dso == nullptr) { const char* err = dlerror(); - ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown"); + ALOGE("load_driver(%s): %s", driverAbsolutePath, err ? err : "unknown"); return nullptr; } - ALOGD("loaded %s", driver_absolute_path); + ALOGV("loaded %s", driverAbsolutePath); return dso; } static void* load_angle(const char* kind, android_namespace_t* ns) { - const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = ns, - }; - std::string name = std::string("lib") + kind + "_angle.so"; + void* so = nullptr; - void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + if (android::GraphicsEnv::getInstance().shouldUseSystemAngle()) { + so = do_dlopen(name.c_str(), RTLD_NOW | RTLD_LOCAL); + } else { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = ns, + }; + so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + } if (so) { return so; @@ -563,10 +584,14 @@ Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) { ATRACE_CALL(); android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); - if (!ns) { + // ANGLE namespace is used for loading ANGLE from apk, and hence if namespace is not + // constructed, it means ANGLE apk is not set to be the OpenGL ES driver. + // Hence skip if ANGLE apk and system ANGLE are not set to be the OpenGL ES driver. + if (!ns && !android::GraphicsEnv::getInstance().shouldUseSystemAngle()) { return nullptr; } + // use ANGLE APK driver android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE); driver_t* hnd = nullptr; @@ -588,10 +613,13 @@ Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) { } void Loader::attempt_to_init_angle_backend(void* dso, egl_connection_t* cnx) { - void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform"); - if (pANGLEGetDisplayPlatform) { + cnx->angleGetDisplayPlatformFunc = dlsym(dso, "ANGLEGetDisplayPlatform"); + cnx->angleResetDisplayPlatformFunc = dlsym(dso, "ANGLEResetDisplayPlatform"); + + if (cnx->angleGetDisplayPlatformFunc) { ALOGV("ANGLE GLES library loaded"); cnx->angleLoaded = true; + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE); } else { ALOGV("Native GLES library loaded"); cnx->angleLoaded = false; @@ -635,7 +663,13 @@ Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, const bool exact) { ATRACE_CALL(); - android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL); + if (suffix && strcmp(suffix, "angle") == 0) { + // use system ANGLE driver + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE); + } else { + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL); + } + driver_t* hnd = nullptr; void* dso = load_system_driver("GLES", suffix, exact); if (dso) { diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp index ed3c616b92..9905210014 100644 --- a/opengl/libs/EGL/MultifileBlobCache.cpp +++ b/opengl/libs/EGL/MultifileBlobCache.cpp @@ -18,6 +18,7 @@ #include "MultifileBlobCache.h" +#include <android-base/properties.h> #include <dirent.h> #include <fcntl.h> #include <inttypes.h> @@ -62,12 +63,15 @@ void freeHotCacheEntry(android::MultifileHotCache& entry) { namespace android { MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize, - const std::string& baseDir) + size_t maxTotalEntries, const std::string& baseDir) : mInitialized(false), + mCacheVersion(0), mMaxKeySize(maxKeySize), mMaxValueSize(maxValueSize), mMaxTotalSize(maxTotalSize), + mMaxTotalEntries(maxTotalEntries), mTotalCacheSize(0), + mTotalCacheEntries(0), mHotCacheLimit(0), mHotCacheSize(0), mWorkerThreadIdle(true) { @@ -76,6 +80,26 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s return; } + // Set the cache version, override if debug value set + mCacheVersion = kMultifileBlobCacheVersion; + int debugCacheVersion = base::GetIntProperty("debug.egl.blobcache.cache_version", -1); + if (debugCacheVersion >= 0) { + ALOGV("INIT: Using %u as cacheVersion instead of %u", debugCacheVersion, mCacheVersion); + mCacheVersion = debugCacheVersion; + } + + // Set the platform build ID, override if debug value set + mBuildId = base::GetProperty("ro.build.id", ""); + std::string debugBuildId = base::GetProperty("debug.egl.blobcache.build_id", ""); + if (!debugBuildId.empty()) { + ALOGV("INIT: Using %s as buildId instead of %s", debugBuildId.c_str(), mBuildId.c_str()); + if (debugBuildId.length() > PROP_VALUE_MAX) { + ALOGV("INIT: debugBuildId is too long (%zu), reduce it to %u", debugBuildId.length(), + PROP_VALUE_MAX); + } + mBuildId = debugBuildId; + } + // Establish the name of our multifile directory mMultifileDirName = baseDir + ".multifile"; @@ -93,14 +117,30 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s mTaskThread = std::thread(&MultifileBlobCache::processTasks, this); // See if the dir exists, and initialize using its contents + bool statusGood = false; + + // Check that our cacheVersion and buildId match struct stat st; if (stat(mMultifileDirName.c_str(), &st) == 0) { + if (checkStatus(mMultifileDirName.c_str())) { + statusGood = true; + } else { + ALOGV("INIT: Cache status has changed, clearing the cache"); + if (!clearCache()) { + ALOGE("INIT: Unable to clear cache"); + return; + } + } + } + + if (statusGood) { // Read all the files and gather details, then preload their contents DIR* dir; struct dirent* entry; if ((dir = opendir(mMultifileDirName.c_str())) != nullptr) { while ((entry = readdir(dir)) != nullptr) { - if (entry->d_name == "."s || entry->d_name == ".."s) { + if (entry->d_name == "."s || entry->d_name == ".."s || + strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) { continue; } @@ -123,7 +163,8 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s if (st.st_size <= 0 || st.st_atime <= 0) { ALOGE("INIT: Entry %u has invalid stats! Removing.", entryHash); if (remove(fullPath.c_str()) != 0) { - ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno)); + ALOGE("INIT: Error removing %s: %s", fullPath.c_str(), + std::strerror(errno)); } continue; } @@ -140,7 +181,7 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s MultifileHeader header; size_t result = read(fd, static_cast<void*>(&header), sizeof(MultifileHeader)); if (result != sizeof(MultifileHeader)) { - ALOGE("Error reading MultifileHeader from cache entry (%s): %s", + ALOGE("INIT: Error reading MultifileHeader from cache entry (%s): %s", fullPath.c_str(), std::strerror(errno)); close(fd); return; @@ -150,7 +191,8 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s if (header.magic != kMultifileMagic) { ALOGE("INIT: Entry %u has bad magic (%u)! Removing.", entryHash, header.magic); if (remove(fullPath.c_str()) != 0) { - ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno)); + ALOGE("INIT: Error removing %s: %s", fullPath.c_str(), + std::strerror(errno)); } close(fd); continue; @@ -175,7 +217,7 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s if (header.crc != crc32c(mappedEntry + sizeof(MultifileHeader), fileSize - sizeof(MultifileHeader))) { - ALOGE("INIT: Entry %u failed CRC check! Removing.", entryHash); + ALOGV("INIT: Entry %u failed CRC check! Removing.", entryHash); if (remove(fullPath.c_str()) != 0) { ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno)); } @@ -184,11 +226,12 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s // If the cache entry is damaged or no good, remove it if (header.keySize <= 0 || header.valueSize <= 0) { - ALOGE("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), " + ALOGV("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), " "removing.", entryHash, header.keySize, header.valueSize); if (remove(fullPath.c_str()) != 0) { - ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno)); + ALOGE("INIT: Error removing %s: %s", fullPath.c_str(), + std::strerror(errno)); } continue; } @@ -226,9 +269,17 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s // If the multifile directory does not exist, create it and start from scratch if (mkdir(mMultifileDirName.c_str(), 0755) != 0 && (errno != EEXIST)) { ALOGE("Unable to create directory (%s), errno (%i)", mMultifileDirName.c_str(), errno); + return; + } + + // Create new status file + if (!createStatus(mMultifileDirName.c_str())) { + ALOGE("INIT: Failed to create status file!"); + return; } } + ALOGV("INIT: Multifile BlobCache initialization succeeded"); mInitialized = true; } @@ -270,7 +321,7 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi size_t fileSize = sizeof(MultifileHeader) + keySize + valueSize; // If we're going to be over the cache limit, kick off a trim to clear space - if (getTotalSize() + fileSize > mMaxTotalSize) { + if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) { ALOGV("SET: Cache is full, calling trimCache to clear space"); trimCache(); } @@ -469,6 +520,112 @@ void MultifileBlobCache::finish() { } } +bool MultifileBlobCache::createStatus(const std::string& baseDir) { + // Populate the status struct + struct MultifileStatus status; + memset(&status, 0, sizeof(status)); + status.magic = kMultifileMagic; + status.cacheVersion = mCacheVersion; + + // Copy the buildId string in, up to our allocated space + strncpy(status.buildId, mBuildId.c_str(), + mBuildId.length() > PROP_VALUE_MAX ? PROP_VALUE_MAX : mBuildId.length()); + + // Finally update the crc, using cacheVersion and everything the follows + status.crc = + crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion), + sizeof(status) - offsetof(MultifileStatus, cacheVersion)); + + // Create the status file + std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile; + int fd = open(cacheStatus.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd == -1) { + ALOGE("STATUS(CREATE): Unable to create status file: %s, error: %s", cacheStatus.c_str(), + std::strerror(errno)); + return false; + } + + // Write the buffer contents to disk + ssize_t result = write(fd, &status, sizeof(status)); + close(fd); + if (result != sizeof(status)) { + ALOGE("STATUS(CREATE): Error writing cache status file: %s, error %s", cacheStatus.c_str(), + std::strerror(errno)); + return false; + } + + ALOGV("STATUS(CREATE): Created status file: %s", cacheStatus.c_str()); + return true; +} + +bool MultifileBlobCache::checkStatus(const std::string& baseDir) { + std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile; + + // Does status exist + struct stat st; + if (stat(cacheStatus.c_str(), &st) != 0) { + ALOGV("STATUS(CHECK): Status file (%s) missing", cacheStatus.c_str()); + return false; + } + + // If the status entry is damaged or no good, remove it + if (st.st_size <= 0 || st.st_atime <= 0) { + ALOGE("STATUS(CHECK): Cache status has invalid stats!"); + return false; + } + + // Open the file so we can read its header + int fd = open(cacheStatus.c_str(), O_RDONLY); + if (fd == -1) { + ALOGE("STATUS(CHECK): Cache error - failed to open cacheStatus: %s, error: %s", + cacheStatus.c_str(), std::strerror(errno)); + return false; + } + + // Read in the status header + MultifileStatus status; + size_t result = read(fd, static_cast<void*>(&status), sizeof(MultifileStatus)); + close(fd); + if (result != sizeof(MultifileStatus)) { + ALOGE("STATUS(CHECK): Error reading cache status (%s): %s", cacheStatus.c_str(), + std::strerror(errno)); + return false; + } + + // Verify header magic + if (status.magic != kMultifileMagic) { + ALOGE("STATUS(CHECK): Cache status has bad magic (%u)!", status.magic); + return false; + } + + // Ensure we have a good CRC + if (status.crc != + crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion), + sizeof(status) - offsetof(MultifileStatus, cacheVersion))) { + ALOGE("STATUS(CHECK): Cache status failed CRC check!"); + return false; + } + + // Check cacheVersion + if (status.cacheVersion != mCacheVersion) { + ALOGV("STATUS(CHECK): Cache version has changed! old(%u) new(%u)", status.cacheVersion, + mCacheVersion); + return false; + } + + // Check buildId + if (strcmp(status.buildId, mBuildId.c_str()) != 0) { + ALOGV("STATUS(CHECK): BuildId has changed! old(%s) new(%s)", status.buildId, + mBuildId.c_str()); + return false; + } + + // All checks passed! + ALOGV("STATUS(CHECK): Status file is good! cacheVersion(%u), buildId(%s) file(%s)", + status.cacheVersion, status.buildId, cacheStatus.c_str()); + return true; +} + void MultifileBlobCache::trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize, time_t accessTime) { mEntries.insert(entryHash); @@ -485,10 +642,12 @@ MultifileEntryStats MultifileBlobCache::getEntryStats(uint32_t entryHash) { void MultifileBlobCache::increaseTotalCacheSize(size_t fileSize) { mTotalCacheSize += fileSize; + mTotalCacheEntries++; } void MultifileBlobCache::decreaseTotalCacheSize(size_t fileSize) { mTotalCacheSize -= fileSize; + mTotalCacheEntries--; } bool MultifileBlobCache::addToHotCache(uint32_t newEntryHash, int newFd, uint8_t* newEntryBuffer, @@ -557,7 +716,7 @@ bool MultifileBlobCache::removeFromHotCache(uint32_t entryHash) { return false; } -bool MultifileBlobCache::applyLRU(size_t cacheLimit) { +bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit) { // Walk through our map of sorted last access times and remove files until under the limit for (auto cacheEntryIter = mEntryStats.begin(); cacheEntryIter != mEntryStats.end();) { uint32_t entryHash = cacheEntryIter->first; @@ -590,9 +749,10 @@ bool MultifileBlobCache::applyLRU(size_t cacheLimit) { // See if it has been reduced enough size_t totalCacheSize = getTotalSize(); - if (totalCacheSize <= cacheLimit) { + size_t totalCacheEntries = getTotalEntries(); + if (totalCacheSize <= cacheSizeLimit && totalCacheEntries <= cacheEntryLimit) { // Success - ALOGV("LRU: Reduced cache to %zu", totalCacheSize); + ALOGV("LRU: Reduced cache to size %zu entries %zu", totalCacheSize, totalCacheEntries); return true; } } @@ -601,8 +761,43 @@ bool MultifileBlobCache::applyLRU(size_t cacheLimit) { return false; } +// Clear the cache by removing all entries and deleting the directory +bool MultifileBlobCache::clearCache() { + DIR* dir; + struct dirent* entry; + dir = opendir(mMultifileDirName.c_str()); + if (dir == nullptr) { + ALOGE("CLEAR: Unable to open multifile dir: %s", mMultifileDirName.c_str()); + return false; + } + + // Delete all entries and the status file + while ((entry = readdir(dir)) != nullptr) { + if (entry->d_name == "."s || entry->d_name == ".."s) { + continue; + } + + std::string entryName = entry->d_name; + std::string fullPath = mMultifileDirName + "/" + entryName; + if (remove(fullPath.c_str()) != 0) { + ALOGE("CLEAR: Error removing %s: %s", fullPath.c_str(), std::strerror(errno)); + return false; + } + } + + // Delete the directory + if (remove(mMultifileDirName.c_str()) != 0) { + ALOGE("CLEAR: Error removing %s: %s", mMultifileDirName.c_str(), std::strerror(errno)); + return false; + } + + ALOGV("CLEAR: Cleared the multifile blobcache"); + return true; +} + // When removing files, what fraction of the overall limit should be reached when removing files // A divisor of two will decrease the cache to 50%, four to 25% and so on +// We use the same limit to manage size and entry count constexpr uint32_t kCacheLimitDivisor = 2; // Calculate the cache size and remove old entries until under the limit @@ -611,8 +806,9 @@ void MultifileBlobCache::trimCache() { ALOGV("TRIM: Waiting for work to complete."); waitForWorkComplete(); - ALOGV("TRIM: Reducing multifile cache size to %zu", mMaxTotalSize / kCacheLimitDivisor); - if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor)) { + ALOGV("TRIM: Reducing multifile cache size to %zu, entries %zu", + mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor); + if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor)) { ALOGE("Error when clearing multifile shader cache"); return; } diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h index 5e527dcf35..18566c2892 100644 --- a/opengl/libs/EGL/MultifileBlobCache.h +++ b/opengl/libs/EGL/MultifileBlobCache.h @@ -21,6 +21,7 @@ #include <EGL/eglext.h> #include <android-base/thread_annotations.h> +#include <cutils/properties.h> #include <future> #include <map> #include <queue> @@ -33,6 +34,9 @@ namespace android { +constexpr uint32_t kMultifileBlobCacheVersion = 1; +constexpr char kMultifileBlobCacheStatusFile[] = "cache.status"; + struct MultifileHeader { uint32_t magic; uint32_t crc; @@ -46,6 +50,13 @@ struct MultifileEntryStats { time_t accessTime; }; +struct MultifileStatus { + uint32_t magic; + uint32_t crc; + uint32_t cacheVersion; + char buildId[PROP_VALUE_MAX]; +}; + struct MultifileHotCache { int entryFd; uint8_t* entryBuffer; @@ -92,7 +103,7 @@ private: class MultifileBlobCache { public: MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize, - const std::string& baseDir); + size_t maxTotalEntries, const std::string& baseDir); ~MultifileBlobCache(); void set(const void* key, EGLsizeiANDROID keySize, const void* value, @@ -103,6 +114,13 @@ public: void finish(); size_t getTotalSize() const { return mTotalCacheSize; } + size_t getTotalEntries() const { return mTotalCacheEntries; } + + const std::string& getCurrentBuildId() const { return mBuildId; } + void setCurrentBuildId(const std::string& buildId) { mBuildId = buildId; } + + uint32_t getCurrentCacheVersion() const { return mCacheVersion; } + void setCurrentCacheVersion(uint32_t cacheVersion) { mCacheVersion = cacheVersion; } private: void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize, @@ -111,6 +129,9 @@ private: bool removeEntry(uint32_t entryHash); MultifileEntryStats getEntryStats(uint32_t entryHash); + bool createStatus(const std::string& baseDir); + bool checkStatus(const std::string& baseDir); + size_t getFileSize(uint32_t entryHash); size_t getValueSize(uint32_t entryHash); @@ -120,12 +141,16 @@ private: bool addToHotCache(uint32_t entryHash, int fd, uint8_t* entryBufer, size_t entrySize); bool removeFromHotCache(uint32_t entryHash); + bool clearCache(); void trimCache(); - bool applyLRU(size_t cacheLimit); + bool applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit); bool mInitialized; std::string mMultifileDirName; + std::string mBuildId; + uint32_t mCacheVersion; + std::unordered_set<uint32_t> mEntries; std::unordered_map<uint32_t, MultifileEntryStats> mEntryStats; std::unordered_map<uint32_t, MultifileHotCache> mHotCache; @@ -133,7 +158,9 @@ private: size_t mMaxKeySize; size_t mMaxValueSize; size_t mMaxTotalSize; + size_t mMaxTotalEntries; size_t mTotalCacheSize; + size_t mTotalCacheEntries; size_t mHotCacheLimit; size_t mHotCacheEntryLimit; size_t mHotCacheSize; diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp index 1639be6480..90a0f1ee06 100644 --- a/opengl/libs/EGL/MultifileBlobCache_test.cpp +++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp @@ -16,13 +16,17 @@ #include "MultifileBlobCache.h" +#include <android-base/properties.h> #include <android-base/test_utils.h> #include <fcntl.h> #include <gtest/gtest.h> #include <stdio.h> +#include <fstream> #include <memory> +using namespace std::literals; + namespace android { template <typename T> @@ -31,23 +35,40 @@ using sp = std::shared_ptr<T>; constexpr size_t kMaxKeySize = 2 * 1024; constexpr size_t kMaxValueSize = 6 * 1024; constexpr size_t kMaxTotalSize = 32 * 1024; +constexpr size_t kMaxTotalEntries = 64; class MultifileBlobCacheTest : public ::testing::Test { protected: virtual void SetUp() { + clearProperties(); mTempFile.reset(new TemporaryFile()); mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, - &mTempFile->path[0])); + kMaxTotalEntries, &mTempFile->path[0])); } - virtual void TearDown() { mMBC.reset(); } + virtual void TearDown() { + clearProperties(); + mMBC.reset(); + } int getFileDescriptorCount(); + std::vector<std::string> getCacheEntries(); + + void clearProperties(); std::unique_ptr<TemporaryFile> mTempFile; std::unique_ptr<MultifileBlobCache> mMBC; }; +void MultifileBlobCacheTest::clearProperties() { + // Clear any debug properties used in the tests + base::SetProperty("debug.egl.blobcache.cache_version", ""); + base::WaitForProperty("debug.egl.blobcache.cache_version", ""); + + base::SetProperty("debug.egl.blobcache.build_id", ""); + base::WaitForProperty("debug.egl.blobcache.build_id", ""); +} + TEST_F(MultifileBlobCacheTest, CacheSingleValueSucceeds) { unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; mMBC->set("abcd", 4, "efgh", 4); @@ -211,6 +232,23 @@ TEST_F(MultifileBlobCacheTest, CacheMaxKeyAndValueSizeSucceeds) { } } +TEST_F(MultifileBlobCacheTest, CacheMaxEntrySucceeds) { + // Fill the cache with max entries + int i = 0; + for (i = 0; i < kMaxTotalEntries; i++) { + mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i)); + } + + // Ensure it is full + ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries); + + // Add another entry + mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i)); + + // Ensure total entries is cut in half + 1 + ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries / 2 + 1); +} + TEST_F(MultifileBlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { unsigned char buf[1] = {0xee}; mMBC->set("x", 1, "y", 1); @@ -234,8 +272,7 @@ int MultifileBlobCacheTest::getFileDescriptorCount() { TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) { // Populate the cache with a bunch of entries - size_t kLargeNumberOfEntries = 1024; - for (int i = 0; i < kLargeNumberOfEntries; i++) { + for (int i = 0; i < kMaxTotalEntries; i++) { // printf("Caching: %i", i); // Use the index as the key and value @@ -247,27 +284,223 @@ TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) { } // Ensure we don't have a bunch of open fds - ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2); + ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2); // Close the cache so everything writes out mMBC->finish(); mMBC.reset(); // Now open it again and ensure we still don't have a bunch of open fds - mMBC.reset( - new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0])); + mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &mTempFile->path[0])); // Check after initialization - ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2); + ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2); - for (int i = 0; i < kLargeNumberOfEntries; i++) { + for (int i = 0; i < kMaxTotalEntries; i++) { int result = 0; ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result))); ASSERT_EQ(i, result); } // And again after we've actually used it - ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2); + ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2); +} + +std::vector<std::string> MultifileBlobCacheTest::getCacheEntries() { + std::string cachePath = &mTempFile->path[0]; + std::string multifileDirName = cachePath + ".multifile"; + std::vector<std::string> cacheEntries; + + struct stat info; + if (stat(multifileDirName.c_str(), &info) == 0) { + // We have a multifile dir. Skip the status file and return the only entry. + DIR* dir; + struct dirent* entry; + if ((dir = opendir(multifileDirName.c_str())) != nullptr) { + while ((entry = readdir(dir)) != nullptr) { + if (entry->d_name == "."s || entry->d_name == ".."s) { + continue; + } + if (strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) { + continue; + } + cacheEntries.push_back(multifileDirName + "/" + entry->d_name); + } + } else { + printf("Unable to open %s, error: %s\n", multifileDirName.c_str(), + std::strerror(errno)); + } + } else { + printf("Unable to stat %s, error: %s\n", multifileDirName.c_str(), std::strerror(errno)); + } + + return cacheEntries; +} + +TEST_F(MultifileBlobCacheTest, CacheContainsStatus) { + struct stat info; + std::stringstream statusFile; + statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile; + + // After INIT, cache should have a status + ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0); + + // Set one entry + mMBC->set("abcd", 4, "efgh", 4); + + // Close the cache so everything writes out + mMBC->finish(); + mMBC.reset(); + + // Ensure status lives after closing the cache + ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0); + + // Open the cache again + mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &mTempFile->path[0])); + + // Ensure we still have a status + ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0); +} + +// Verify missing cache status file causes cache the be cleared +TEST_F(MultifileBlobCacheTest, MissingCacheStatusClears) { + // Set one entry + mMBC->set("abcd", 4, "efgh", 4); + + // Close the cache so everything writes out + mMBC->finish(); + mMBC.reset(); + + // Ensure there is one cache entry + ASSERT_EQ(getCacheEntries().size(), 1); + + // Delete the status file + std::stringstream statusFile; + statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile; + remove(statusFile.str().c_str()); + + // Open the cache again and ensure no cache hits + mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &mTempFile->path[0])); + + // Ensure we have no entries + ASSERT_EQ(getCacheEntries().size(), 0); +} + +// Verify modified cache status file BEGIN causes cache to be cleared +TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusBeginClears) { + // Set one entry + mMBC->set("abcd", 4, "efgh", 4); + + // Close the cache so everything writes out + mMBC->finish(); + mMBC.reset(); + + // Ensure there is one cache entry + ASSERT_EQ(getCacheEntries().size(), 1); + + // Modify the status file + std::stringstream statusFile; + statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile; + + // Stomp on the beginning of the cache file + const char* stomp = "BADF00D"; + std::fstream fs(statusFile.str()); + fs.seekp(0, std::ios_base::beg); + fs.write(stomp, strlen(stomp)); + fs.flush(); + fs.close(); + + // Open the cache again and ensure no cache hits + mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &mTempFile->path[0])); + + // Ensure we have no entries + ASSERT_EQ(getCacheEntries().size(), 0); +} + +// Verify modified cache status file END causes cache to be cleared +TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusEndClears) { + // Set one entry + mMBC->set("abcd", 4, "efgh", 4); + + // Close the cache so everything writes out + mMBC->finish(); + mMBC.reset(); + + // Ensure there is one cache entry + ASSERT_EQ(getCacheEntries().size(), 1); + + // Modify the status file + std::stringstream statusFile; + statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile; + + // Stomp on the END of the cache status file, modifying its contents + const char* stomp = "BADF00D"; + std::fstream fs(statusFile.str()); + fs.seekp(-strlen(stomp), std::ios_base::end); + fs.write(stomp, strlen(stomp)); + fs.flush(); + fs.close(); + + // Open the cache again and ensure no cache hits + mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &mTempFile->path[0])); + + // Ensure we have no entries + ASSERT_EQ(getCacheEntries().size(), 0); +} + +// Verify mismatched cacheVersion causes cache to be cleared +TEST_F(MultifileBlobCacheTest, MismatchedCacheVersionClears) { + // Set one entry + mMBC->set("abcd", 4, "efgh", 4); + + // Close the cache so everything writes out + mMBC->finish(); + mMBC.reset(); + + // Ensure there is one cache entry + ASSERT_EQ(getCacheEntries().size(), 1); + + // Set a debug cacheVersion + std::string newCacheVersion = std::to_string(kMultifileBlobCacheVersion + 1); + ASSERT_TRUE(base::SetProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str())); + ASSERT_TRUE( + base::WaitForProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str())); + + // Open the cache again and ensure no cache hits + mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &mTempFile->path[0])); + + // Ensure we have no entries + ASSERT_EQ(getCacheEntries().size(), 0); +} + +// Verify mismatched buildId causes cache to be cleared +TEST_F(MultifileBlobCacheTest, MismatchedBuildIdClears) { + // Set one entry + mMBC->set("abcd", 4, "efgh", 4); + + // Close the cache so everything writes out + mMBC->finish(); + mMBC.reset(); + + // Ensure there is one cache entry + ASSERT_EQ(getCacheEntries().size(), 1); + + // Set a debug buildId + base::SetProperty("debug.egl.blobcache.build_id", "foo"); + base::WaitForProperty("debug.egl.blobcache.build_id", "foo"); + + // Open the cache again and ensure no cache hits + mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &mTempFile->path[0])); + + // Ensure we have no entries + ASSERT_EQ(getCacheEntries().size(), 0); } } // namespace android diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp index ee605c2011..f0054a7cf7 100644 --- a/opengl/libs/EGL/egl_angle_platform.cpp +++ b/opengl/libs/EGL/egl_angle_platform.cpp @@ -35,12 +35,6 @@ namespace angle { -constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so"; -constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW; - -static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr; -static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr; - static time_t startTime = time(nullptr); static const unsigned char* getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/, @@ -111,50 +105,19 @@ static void assignAnglePlatformMethods(PlatformMethods* platformMethods) { } // Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace -bool initializeAnglePlatform(EGLDisplay dpy) { - // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform - android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); - void* so = nullptr; - if (ns) { - const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = ns, - }; - so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo); - if (so) { - ALOGD("dlopen_ext from APK (%s) success at %p", kAngleEs2Lib, so); - } else { - ALOGE("dlopen_ext(\"%s\") failed: %s", kAngleEs2Lib, dlerror()); - return false; - } - } else { - // If we are here, ANGLE is loaded as built-in gl driver in the sphal. - so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags); - if (so) { - ALOGD("dlopen (%s) success at %p", kAngleEs2Lib, so); - } else { - ALOGE("%s failed to dlopen %s: %s!", __FUNCTION__, kAngleEs2Lib, dlerror()); - return false; - } - } - - angleGetDisplayPlatform = - reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform")); - - if (!angleGetDisplayPlatform) { - ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!"); - dlclose(so); +bool initializeAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx) { + if (cnx->angleGetDisplayPlatformFunc == nullptr) { + ALOGE("ANGLEGetDisplayPlatform is not initialized!"); return false; } - angleResetDisplayPlatform = - reinterpret_cast<ResetDisplayPlatformFunc>(dlsym(so, "ANGLEResetDisplayPlatform")); + GetDisplayPlatformFunc angleGetDisplayPlatform = + reinterpret_cast<GetDisplayPlatformFunc>(cnx->angleGetDisplayPlatformFunc); PlatformMethods* platformMethods = nullptr; if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr, &platformMethods))) { ALOGE("ANGLEGetDisplayPlatform call failed!"); - dlclose(so); return false; } if (platformMethods) { @@ -166,8 +129,10 @@ bool initializeAnglePlatform(EGLDisplay dpy) { return true; } -void resetAnglePlatform(EGLDisplay dpy) { - if (angleResetDisplayPlatform) { +void resetAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx) { + if (cnx->angleResetDisplayPlatformFunc) { + ResetDisplayPlatformFunc angleResetDisplayPlatform = + reinterpret_cast<ResetDisplayPlatformFunc>(cnx->angleResetDisplayPlatformFunc); angleResetDisplayPlatform(dpy); } } diff --git a/opengl/libs/EGL/egl_angle_platform.h b/opengl/libs/EGL/egl_angle_platform.h index 6c24aa5418..63806c2a4c 100644 --- a/opengl/libs/EGL/egl_angle_platform.h +++ b/opengl/libs/EGL/egl_angle_platform.h @@ -25,8 +25,8 @@ namespace angle { -bool initializeAnglePlatform(EGLDisplay dpy); -void resetAnglePlatform(EGLDisplay dpy); +bool initializeAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx); +void resetAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx); }; // namespace angle diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 1b68344cb1..98ff1d12cc 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -41,6 +41,7 @@ static const unsigned int kDeferredMonolithicSaveDelay = 4; constexpr uint32_t kMaxMultifileKeySize = 1 * 1024 * 1024; constexpr uint32_t kMaxMultifileValueSize = 8 * 1024 * 1024; constexpr uint32_t kMaxMultifileTotalSize = 32 * 1024 * 1024; +constexpr uint32_t kMaxMultifileTotalEntries = 4 * 1024; namespace android { @@ -277,7 +278,7 @@ MultifileBlobCache* egl_cache_t::getMultifileBlobCacheLocked() { if (mMultifileBlobCache == nullptr) { mMultifileBlobCache.reset(new MultifileBlobCache(kMaxMultifileKeySize, kMaxMultifileValueSize, mCacheByteLimit, - mFilename)); + kMaxMultifileTotalEntries, mFilename)); } return mMultifileBlobCache.get(); } diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 3317347066..55a2682ac0 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -168,7 +168,7 @@ static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_conn if (dpy == EGL_NO_DISPLAY) { ALOGE("eglGetPlatformDisplay failed!"); } else { - if (!angle::initializeAnglePlatform(dpy)) { + if (!angle::initializeAnglePlatform(dpy, cnx)) { ALOGE("initializeAnglePlatform failed!"); } } @@ -433,7 +433,7 @@ EGLBoolean egl_display_t::terminate() { if (cnx->dso && disp.state == egl_display_t::INITIALIZED) { // If we're using ANGLE reset any custom DisplayPlatform if (cnx->angleLoaded) { - angle::resetAnglePlatform(disp.dpy); + angle::resetAnglePlatform(disp.dpy, cnx); } if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) { ALOGW("eglTerminate(%p) failed (%s)", disp.dpy, diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h index 3bd37cb399..90a3c199a9 100644 --- a/opengl/libs/EGL/egldefs.h +++ b/opengl/libs/EGL/egldefs.h @@ -42,7 +42,9 @@ struct egl_connection_t { libGles1(nullptr), libGles2(nullptr), systemDriverUnloaded(false), - angleLoaded(false) { + angleLoaded(false), + angleGetDisplayPlatformFunc(nullptr), + angleResetDisplayPlatformFunc(nullptr) { const char* const* entries = platform_names; EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform); while (*entries) { @@ -75,6 +77,9 @@ struct egl_connection_t { bool systemDriverUnloaded; bool angleLoaded; // Was ANGLE successfully loaded + + void* angleGetDisplayPlatformFunc; + void* angleResetDisplayPlatformFunc; }; extern gl_hooks_t gHooks[2]; diff --git a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp index 633cc9c51b..2d9ee3a49e 100644 --- a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp +++ b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp @@ -28,6 +28,7 @@ namespace android { constexpr size_t kMaxKeySize = 2 * 1024; constexpr size_t kMaxValueSize = 6 * 1024; constexpr size_t kMaxTotalSize = 32 * 1024; +constexpr size_t kMaxTotalEntries = 64; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // To fuzz this, we're going to create a key/value pair from data @@ -79,8 +80,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { std::unique_ptr<MultifileBlobCache> mbc; tempFile.reset(new TemporaryFile()); - mbc.reset( - new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0])); + mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &tempFile->path[0])); // With remaining data, select different paths below int loopCount = 1; uint8_t bumpCount = 0; @@ -131,8 +132,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Place the maxKey/maxValue twice // The first will fit, the second will trigger hot cache trimming tempFile.reset(new TemporaryFile()); - mbc.reset( - new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0])); + mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &tempFile->path[0])); uint8_t* buffer = new uint8_t[kMaxValueSize]; mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize); mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize); @@ -145,7 +146,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // overflow tempFile.reset(new TemporaryFile()); mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, 2 * (kMaxKeySize + kMaxValueSize), - &tempFile->path[0])); + kMaxTotalEntries, &tempFile->path[0])); mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize); mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize); mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize); diff --git a/opengl/tests/EGLTest/egl_cache_test.cpp b/opengl/tests/EGLTest/egl_cache_test.cpp index f81c68f66e..ce5818229a 100644 --- a/opengl/tests/EGLTest/egl_cache_test.cpp +++ b/opengl/tests/EGLTest/egl_cache_test.cpp @@ -114,25 +114,26 @@ std::string EGLCacheTest::getCachefileName() { struct stat info; if (stat(multifileDirName.c_str(), &info) == 0) { // Ensure we only have one file to manage - int realFileCount = 0; + int entryFileCount = 0; - // We have a multifile dir. Return the only real file in it. + // We have a multifile dir. Return the only entry file in it. DIR* dir; struct dirent* entry; if ((dir = opendir(multifileDirName.c_str())) != nullptr) { while ((entry = readdir(dir)) != nullptr) { - if (entry->d_name == "."s || entry->d_name == ".."s) { + if (entry->d_name == "."s || entry->d_name == ".."s || + strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) { continue; } cachefileName = multifileDirName + "/" + entry->d_name; - realFileCount++; + entryFileCount++; } } else { printf("Unable to open %s, error: %s\n", multifileDirName.c_str(), std::strerror(errno)); } - if (realFileCount != 1) { + if (entryFileCount != 1) { // If there was more than one real file in the directory, this // violates test assumptions cachefileName = ""; diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp index f06a0457d3..11b636d564 100644 --- a/services/gpuservice/gpustats/GpuStats.cpp +++ b/services/gpuservice/gpustats/GpuStats.cpp @@ -163,11 +163,13 @@ void GpuStats::insertDriverStats(const std::string& driverPackageName, addLoadingTime(driver, driverLoadingTime, &appInfo); appInfo.appPackageName = appPackageName; appInfo.driverVersionCode = driverVersionCode; - appInfo.angleInUse = driverPackageName == "angle"; + appInfo.angleInUse = + driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle"; appInfo.lastAccessTime = std::chrono::system_clock::now(); mAppStats.insert({appStatsKey, appInfo}); } else { - mAppStats[appStatsKey].angleInUse = driverPackageName == "angle"; + mAppStats[appStatsKey].angleInUse = + driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle"; addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]); mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now(); } diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 76729efb0a..b5f5df95a9 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -243,6 +243,9 @@ phony { "Bug-115739809", "StructLayout_test", + // jni + "libservices.core", + // rust targets "libinput_rust_test", @@ -256,6 +259,7 @@ phony { "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 e411abb251..e529bddea5 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -16,17 +16,51 @@ #define LOG_TAG "PointerChoreographer" +#include <android-base/logging.h> +#include <input/PrintTools.h> + #include "PointerChoreographer.h" +#define INDENT " " + namespace android { +namespace { +bool isFromMouse(const NotifyMotionArgs& args) { + return isFromSource(args.source, AINPUT_SOURCE_MOUSE) && + 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), + mShowTouchesEnabled(false), + mStylusPointerIconEnabled(false) {} void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) { + std::scoped_lock _l(mLock); + + mInputDeviceInfos = args.inputDeviceInfos; + updatePointerControllersLocked(); mNextListener.notify(args); } @@ -39,7 +73,127 @@ void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) { } void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) { - mNextListener.notify(args); + NotifyMotionArgs newArgs = processMotion(args); + + mNextListener.notify(newArgs); +} + +NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) { + std::scoped_lock _l(mLock); + + if (isFromMouse(args)) { + return processMouseEventLocked(args); + } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) { + processStylusHoverEventLocked(args); + } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) { + processTouchscreenAndStylusEventLocked(args); + } + return args; +} + +NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) { + if (args.getPointerCount() != 1) { + LOG(FATAL) << "Only mouse events with a single pointer are currently supported: " + << args.dump(); + } + + const int32_t displayId = getTargetMouseDisplayLocked(args.displayId); + + // Get the mouse pointer controller for the display, or create one if it doesn't exist. + auto [it, emplaced] = + mMousePointersByDisplay.try_emplace(displayId, + getMouseControllerConstructor(displayId)); + if (emplaced) { + notifyPointerDisplayIdChangedLocked(); + } + + PointerControllerInterface& pc = *it->second; + + const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + pc.move(deltaX, deltaY); + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + + const auto [x, y] = pc.getPosition(); + NotifyMotionArgs newArgs(args); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + newArgs.xCursorPosition = x; + newArgs.yCursorPosition = y; + newArgs.displayId = displayId; + return newArgs; +} + +/** + * When screen is touched, fade the mouse pointer on that display. We only call fade for + * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the + * mouse device keeps moving and unfades the cursor. + * For touch events, we do not need to populate the cursor position. + */ +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); + } + + 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) { @@ -55,16 +209,222 @@ void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& ar } 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) { + std::scoped_lock _l(mLock); + for (const auto& [_, mousePointerController] : mMousePointersByDisplay) { + mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); + } + } mNextListener.notify(args); } void PointerChoreographer::dump(std::string& dump) { + 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"; +} + +const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const { + for (auto& viewport : mViewports) { + if (viewport.displayId == displayId) { + return &viewport; + } + } + return nullptr; +} + +int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const { + 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) { + const uint32_t sources = info.getSources(); + if (isFromSource(sources, AINPUT_SOURCE_MOUSE) || + isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) { + const int32_t resolvedDisplayId = + 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. + std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) { + 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. + notifyPointerDisplayIdChangedLocked(); +} + +void PointerChoreographer::notifyPointerDisplayIdChangedLocked() { + int32_t displayIdToNotify = ADISPLAY_ID_NONE; + FloatPoint cursorPosition = {0, 0}; + if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId); + it != mMousePointersByDisplay.end()) { + const auto& pointerController = it->second; + // Use the displayId from the pointerController, because it accurately reflects whether + // the viewport has been added for that display. Otherwise, we would have to check if + // the viewport exists separately. + displayIdToNotify = pointerController->getDisplayId(); + cursorPosition = pointerController->getPosition(); + } + + if (mNotifiedPointerDisplayId == displayIdToNotify) { + return; + } + mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition); + mNotifiedPointerDisplayId = displayIdToNotify; +} + +void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) { + std::scoped_lock _l(mLock); + + mDefaultMouseDisplayId = displayId; + updatePointerControllersLocked(); +} + +void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) { + std::scoped_lock _l(mLock); + for (const auto& viewport : viewports) { + 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(); +} + +std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice( + int32_t associatedDisplayId) { + std::scoped_lock _l(mLock); + const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId); + if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) { + return *viewport; + } + return std::nullopt; +} + +FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) { + std::scoped_lock _l(mLock); + const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId); + if (auto it = mMousePointersByDisplay.find(resolvedDisplayId); + it != mMousePointersByDisplay.end()) { + return it->second->getPosition(); + } + 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 = + [this, displayId]() REQUIRES(mLock) { + auto pc = mPolicy.createPointerController( + PointerControllerInterface::ControllerType::MOUSE); + if (const auto viewport = findViewportByIdLocked(displayId); viewport) { + pc->setDisplayViewport(*viewport); + } + return pc; + }; + 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 5e5f78257b..26d2fef5c3 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -20,9 +20,26 @@ #include "NotifyArgs.h" #include "PointerChoreographerPolicyInterface.h" +#include <android-base/thread_annotations.h> +#include <type_traits> + namespace android { /** + * A helper class that wraps a factory method that acts as a constructor for the type returned + * by the factory method. + */ +template <typename Factory> +struct ConstructorDelegate { + constexpr ConstructorDelegate(Factory&& factory) : mFactory(std::move(factory)) {} + + using ConstructedType = std::invoke_result_t<const Factory&>; + constexpr operator ConstructedType() const { return mFactory(); } + + Factory mFactory; +}; + +/** * PointerChoreographer manages the icons shown by the system for input interactions. * This includes showing the mouse cursor, stylus hover icons, and touch spots. * It is responsible for accumulating the location of the mouse cursor, and populating @@ -31,6 +48,17 @@ namespace android { class PointerChoreographerInterface : public InputListenerInterface { public: /** + * Set the display that pointers, like the mouse cursor and drawing tablets, + * should be drawn on. + */ + virtual void setDefaultMouseDisplayId(int32_t displayId) = 0; + virtual void setDisplayViewports(const std::vector<DisplayViewport>& viewports) = 0; + 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). */ virtual void dump(std::string& dump) = 0; @@ -42,6 +70,14 @@ public: PointerChoreographerPolicyInterface&); ~PointerChoreographer() override = default; + void setDefaultMouseDisplayId(int32_t displayId) override; + void setDisplayViewports(const std::vector<DisplayViewport>& viewports) override; + 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; void notifyKey(const NotifyKeyArgs& args) override; @@ -55,7 +91,42 @@ public: void dump(std::string& dump) override; private: + void updatePointerControllersLocked() REQUIRES(mLock); + 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); + 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; + InputListenerInterface& mNextListener; + PointerChoreographerPolicyInterface& mPolicy; + + 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/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp index ee0ab33559..d9d0450148 100644 --- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp +++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp @@ -15,10 +15,15 @@ */ #include "PreferStylusOverTouchBlocker.h" +#include <com_android_input_flags.h> #include <input/PrintTools.h> +namespace input_flags = com::android::input::flags; + namespace android { +const bool BLOCK_TOUCH_WHEN_STYLUS_HOVER = !input_flags::disable_reject_touch_on_stylus_hover(); + static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) { bool hasStylus = false; bool hasTouch = false; @@ -96,8 +101,11 @@ static void intersectInPlace(std::map<K, V>& map, const std::set<K>& set2) { std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( const NotifyMotionArgs& args) { const auto [hasTouch, hasStylus] = checkToolType(args); - const bool isUpOrCancel = - args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL; + const bool isDisengageOrCancel = BLOCK_TOUCH_WHEN_STYLUS_HOVER + ? (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT || + args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL) + : (args.action == AMOTION_EVENT_ACTION_UP || + args.action == AMOTION_EVENT_ACTION_CANCEL); if (hasTouch && hasStylus) { mDevicesWithMixedToolType.insert(args.deviceId); @@ -109,7 +117,7 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( if (mCanceledDevices.find(args.deviceId) != mCanceledDevices.end()) { // If we started to cancel events from this device, continue to do so to keep // the stream consistent. It should happen at most once per "mixed" device. - if (isUpOrCancel) { + if (isDisengageOrCancel) { mCanceledDevices.erase(args.deviceId); mLastTouchEvents.erase(args.deviceId); } @@ -119,10 +127,13 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( } const bool isStylusEvent = hasStylus; - const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN; + const bool isEngage = BLOCK_TOUCH_WHEN_STYLUS_HOVER + ? (args.action == AMOTION_EVENT_ACTION_DOWN || + args.action == AMOTION_EVENT_ACTION_HOVER_ENTER) + : (args.action == AMOTION_EVENT_ACTION_DOWN); if (isStylusEvent) { - if (isDown) { + if (isEngage) { // Reject all touch while stylus is down mActiveStyli.insert(args.deviceId); @@ -143,7 +154,7 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( result.push_back(args); return result; } - if (isUpOrCancel) { + if (isDisengageOrCancel) { mActiveStyli.erase(args.deviceId); } // Never drop stylus events @@ -158,7 +169,7 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( } const bool shouldDrop = mCanceledDevices.find(args.deviceId) != mCanceledDevices.end(); - if (isUpOrCancel) { + if (isDisengageOrCancel) { mCanceledDevices.erase(args.deviceId); mLastTouchEvents.erase(args.deviceId); } @@ -169,7 +180,7 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( return {}; } - if (!isUpOrCancel) { + if (!isDisengageOrCancel) { mLastTouchEvents[args.deviceId] = args; } return {args}; diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index 6f092a6098..513cbfa7cc 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/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp index 0f6232477e..1e2b9b3ad3 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -67,6 +67,16 @@ const bool DEBUG_OUTBOUND_MOTION = const bool DEBUG_MODEL = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Model", ANDROID_LOG_INFO); +/** + * When multi-device input is enabled, we shouldn't use PreferStylusOverTouchBlocker at all. + * However, multi-device input has the following default behaviour: hovering stylus rejects touch. + * Therefore, if we want to disable that behaviour (and go back to a place where stylus down + * blocks touch, but hovering stylus doesn't interact with touch), we should just disable the entire + * multi-device input feature. + */ +const bool ENABLE_MULTI_DEVICE_INPUT = input_flags::enable_multi_device_input() && + !input_flags::disable_reject_touch_on_stylus_hover(); + // Category (=namespace) name for the input settings that are applied at boot time static const char* INPUT_NATIVE_BOOT = "input_native_boot"; /** @@ -347,7 +357,7 @@ void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs& args) { ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args.dump().c_str()); { // acquire lock std::scoped_lock lock(mLock); - if (input_flags::enable_multi_device_input()) { + if (ENABLE_MULTI_DEVICE_INPUT) { notifyMotionLocked(args); } else { const std::vector<NotifyMotionArgs> processedArgs = diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 188d5f048d..5ae3715f2f 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 android::inputdispatcher { 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 @@ static void benchmarkNotifyMotion(benchmark::State& state) { // 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 @@ static void benchmarkNotifyMotion(benchmark::State& state) { motionArgs.eventTime = now(); dispatcher.notifyMotion(motionArgs); - window->consumeEvent(); - window->consumeEvent(); + window->consumeMotion(); + window->consumeMotion(); } dispatcher.stop(); @@ -298,7 +146,7 @@ static void benchmarkInjectMotion(benchmark::State& state) { // 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 @@ static void benchmarkInjectMotion(benchmark::State& state) { 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 @@ static void benchmarkOnWindowInfosChanged(benchmark::State& state) { // 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/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h index c7d98ab31a..c889b9b136 100644 --- a/services/inputflinger/dispatcher/DebugConfig.h +++ b/services/inputflinger/dispatcher/DebugConfig.h @@ -69,6 +69,16 @@ const bool DEBUG_INJECTION = android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Injection"); /** + * Generally, we always log whenever events are dropped. However, to reduce logspam, some messages + * are suppressed. + * Log additional debug messages about dropped input events with this flag. + * Enable this via "adb shell setprop log.tag.InputDispatcherDroppedEventsVerbose DEBUG". + * Requires system_server restart via `adb shell stop && adb shell start`. + */ +const bool DEBUG_DROPPED_EVENTS_VERBOSE = + android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "DroppedEventsVerbose"); + +/** * Log debug messages about input focus tracking. * Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart) */ diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 30e68024f9..2d1a22b3e6 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -67,24 +67,11 @@ EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policy 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 @@ std::string ConfigurationChangedEntry::getDescription() const { 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 @@ FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToke hasFocus(hasFocus), reason(reason) {} -FocusEntry::~FocusEntry() {} - std::string FocusEntry::getDescription() const { return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false"); } @@ -125,8 +108,6 @@ PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t event : 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 @@ DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, 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 @@ KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t sou 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 @@ std::string KeyEntry::getDescription() const { 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 @@ TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, 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 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 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 @@ SensorEntry::SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 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 b341784eda..d44a211535 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -48,7 +48,7 @@ struct EventEntry { 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 @@ struct EventEntry { 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 @@ struct DeviceResetEntry : EventEntry { DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId); std::string getDescription() const override; - - ~DeviceResetEntry() override; }; struct FocusEntry : EventEntry { @@ -102,8 +95,6 @@ struct FocusEntry : EventEntry { 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 @@ struct PointerCaptureChangedEntry : EventEntry { PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&); std::string getDescription() const override; - - ~PointerCaptureChangedEntry() override; }; struct DragEntry : EventEntry { @@ -123,8 +112,6 @@ struct DragEntry : EventEntry { 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 @@ struct KeyEntry : EventEntry { 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 @@ struct MotionEntry : EventEntry { 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 @@ struct SensorEntry : EventEntry { InputDeviceSensorAccuracy accuracy, bool accuracyChanged, std::vector<float> values); std::string getDescription() const override; - - ~SensorEntry() override; }; struct TouchModeEntry : EventEntry { @@ -219,8 +200,6 @@ struct TouchModeEntry : EventEntry { 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 053594b253..f693dcf8e4 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 3a3f5aefba..8225dec87a 100644 --- a/services/inputflinger/dispatcher/InjectionState.h +++ b/services/inputflinger/dispatcher/InjectionState.h @@ -24,18 +24,12 @@ namespace android { 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 341d146249..23ac088f7f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -72,6 +72,8 @@ using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; namespace input_flags = com::android::input::flags; +static const bool REMOVE_APP_SWITCH_DROPS = input_flags::remove_app_switch_drops(); + namespace android::inputdispatcher { namespace { @@ -390,21 +392,17 @@ std::unique_ptr<DispatchEntry> createDispatchEntry( } 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, @@ -951,20 +949,25 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // Optimize latency of app switches. // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has // been pressed. When it expires, we preempt dispatch and drop all other pending events. - bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; - if (mAppSwitchDueTime < *nextWakeupTime) { - *nextWakeupTime = mAppSwitchDueTime; + bool isAppSwitchDue; + if (!REMOVE_APP_SWITCH_DROPS) { + isAppSwitchDue = mAppSwitchDueTime <= currentTime; + if (mAppSwitchDueTime < *nextWakeupTime) { + *nextWakeupTime = mAppSwitchDueTime; + } } // Ready to start a new event. // If we don't already have a pending event, go grab one. if (!mPendingEvent) { if (mInboundQueue.empty()) { - if (isAppSwitchDue) { - // The inbound queue is empty so the app switch key we were waiting - // for will never arrive. Stop waiting for it. - resetPendingAppSwitchLocked(false); - isAppSwitchDue = false; + if (!REMOVE_APP_SWITCH_DROPS) { + if (isAppSwitchDue) { + // The inbound queue is empty so the app switch key we were waiting + // for will never arrive. Stop waiting for it. + resetPendingAppSwitchLocked(false); + isAppSwitchDue = false; + } } // Synthesize a key repeat if appropriate. @@ -1062,12 +1065,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::Type::KEY: { std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent); - if (isAppSwitchDue) { - if (isAppSwitchKeyEvent(*keyEntry)) { - resetPendingAppSwitchLocked(true); - isAppSwitchDue = false; - } else if (dropReason == DropReason::NOT_DROPPED) { - dropReason = DropReason::APP_SWITCH; + if (!REMOVE_APP_SWITCH_DROPS) { + if (isAppSwitchDue) { + if (isAppSwitchKeyEvent(*keyEntry)) { + resetPendingAppSwitchLocked(true); + isAppSwitchDue = false; + } else if (dropReason == DropReason::NOT_DROPPED) { + dropReason = DropReason::APP_SWITCH; + } } } if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) { @@ -1083,8 +1088,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::Type::MOTION: { std::shared_ptr<MotionEntry> motionEntry = std::static_pointer_cast<MotionEntry>(mPendingEvent); - if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { - dropReason = DropReason::APP_SWITCH; + if (!REMOVE_APP_SWITCH_DROPS) { + if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { + dropReason = DropReason::APP_SWITCH; + } } if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) { dropReason = DropReason::STALE; @@ -1099,8 +1106,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::Type::SENSOR: { std::shared_ptr<SensorEntry> sensorEntry = std::static_pointer_cast<SensorEntry>(mPendingEvent); - if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { - dropReason = DropReason::APP_SWITCH; + if (!REMOVE_APP_SWITCH_DROPS) { + if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { + dropReason = DropReason::APP_SWITCH; + } } // Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use // 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead. @@ -1202,21 +1211,23 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE // If the application takes too long to catch up then we drop all events preceding // the app switch key. const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry); - if (isAppSwitchKeyEvent(keyEntry)) { - if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) { - mAppSwitchSawKeyDown = true; - } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) { - if (mAppSwitchSawKeyDown) { - if (DEBUG_APP_SWITCH) { - ALOGD("App switch is pending!"); + + if (!REMOVE_APP_SWITCH_DROPS) { + if (isAppSwitchKeyEvent(keyEntry)) { + if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) { + mAppSwitchSawKeyDown = true; + } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) { + if (mAppSwitchSawKeyDown) { + if (DEBUG_APP_SWITCH) { + ALOGD("App switch is pending!"); + } + mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT; + mAppSwitchSawKeyDown = false; + needWake = true; } - mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT; - mAppSwitchSawKeyDown = false; - needWake = true; } } } - // If a new up event comes in, and the pending event with same key code has been asked // to try again later because of the policy. We have to reset the intercept key wake up // time for it may have been handled in the policy and could be dropped. @@ -1477,7 +1488,7 @@ void InputDispatcher::releasePendingEventLocked() { } 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."); @@ -1503,10 +1514,11 @@ std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cur (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; @@ -2027,10 +2039,10 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } else { - if (DEBUG_FOCUS) { - ALOGD("Dropping event delivery to target with channel '%s' because it " - "is no longer registered with the input dispatcher.", - inputTarget.inputChannel->getName().c_str()); + if (DEBUG_DROPPED_EVENTS_VERBOSE) { + LOG(INFO) << "Dropping event delivery to target with channel " + << inputTarget.inputChannel->getName() + << " because it is no longer registered with the input dispatcher."; } } } @@ -2448,10 +2460,11 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // If the pointer is not currently down, then ignore the event. if (!tempTouchState.isDown(entry.deviceId) && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { - LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId - << " is not down or we previously " - "dropped the pointer down event in display " - << displayId << ": " << entry.getDescription(); + if (DEBUG_DROPPED_EVENTS_VERBOSE) { + LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId + << " is not down or we previously dropped the pointer down event in " + << "display " << displayId << ": " << entry.getDescription(); + } outInjectionResult = InputEventInjectionResult::FAILED; return {}; } @@ -4231,7 +4244,8 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( ").", 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, @@ -4246,11 +4260,6 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( originalMotionEntry.yCursorPosition, splitDownTime, splitPointerProperties, splitPointerCoords); - if (originalMotionEntry.injectionState) { - splitMotionEntry->injectionState = originalMotionEntry.injectionState; - splitMotionEntry->injectionState->refCount += 1; - } - return splitMotionEntry; } @@ -4338,9 +4347,10 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { } 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(); @@ -4424,7 +4434,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId); if (touchStateIt != mTouchStatesByDisplay.end()) { const TouchState& touchState = touchStateIt->second; - if (touchState.hasTouchingPointers(args.deviceId)) { + if (touchState.hasTouchingPointers(args.deviceId) || + touchState.hasHoveringPointers(args.deviceId)) { policyFlags |= POLICY_FLAG_PASS_TO_USER; } } @@ -4457,14 +4468,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { // 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 && @@ -4610,6 +4621,9 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev 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: { @@ -4642,10 +4656,11 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev 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)); @@ -4685,9 +4700,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev 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(), @@ -4707,9 +4723,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev 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(), @@ -4731,14 +4748,6 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev 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) { @@ -4801,8 +4810,6 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev } } } - - injectionState->release(); } // release lock if (DEBUG_INJECTION) { @@ -4848,37 +4855,40 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu 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 (!entry.injectionState) { + // Not an injected event. + return; + } - 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& injectionState = *entry.injectionState; + if (DEBUG_INJECTION) { + LOG(INFO) << "Setting input event injection result to " + << ftl::enum_string(injectionResult); + } - injectionState->injectionResult = injectionResult; - mInjectionResultAvailable.notify_all(); + 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( @@ -4905,18 +4915,16 @@ void InputDispatcher::transformMotionEntryForInjectionLocked( } 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(); } } @@ -5802,6 +5810,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += INDENT "Connections: <none>\n"; } + dump += "input_flags::remove_app_switch_drops() = "; + dump += toString(REMOVE_APP_SWITCH_DROPS); + dump += "\n"; if (isAppSwitchPendingLocked()) { dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n", ns2ms(mAppSwitchDueTime - now())); @@ -6133,7 +6144,7 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, 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 = @@ -6157,15 +6168,9 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } 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. @@ -6189,12 +6194,13 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } } 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. @@ -6379,15 +6385,15 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec 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. @@ -6447,7 +6453,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable( "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. @@ -6472,7 +6478,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable( if (connection->status != Connection::Status::NORMAL) { connection->inputState.removeFallbackKey(originalKeyCode); - return false; + return {}; } // Latch the fallback keycode for this key on an initial down. @@ -6533,25 +6539,22 @@ bool InputDispatcher::afterKeyEventLockedInterruptable( } 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."); @@ -6561,13 +6564,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable( 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() { @@ -6930,11 +6927,12 @@ sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow( return nullptr; } -void InputDispatcher::setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { +void InputDispatcher::setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, + std::chrono::nanoseconds delay) { std::scoped_lock _l(mLock); - mConfig.keyRepeatTimeout = timeout; - mConfig.keyRepeatDelay = delay; + mConfig.keyRepeatTimeout = timeout.count(); + mConfig.keyRepeatDelay = delay.count(); } } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 1be85f7868..574cf8bee2 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -145,7 +145,8 @@ public: // Public to allow tests to verify that a Monitor can get ANR. void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout); - void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) override; + void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, + std::chrono::nanoseconds delay) override; private: enum class DropReason { @@ -659,12 +660,10 @@ private: 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 09b5186a69..17f0b8737b 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -24,19 +24,6 @@ namespace android::inputdispatcher { -namespace { -bool isHoverAction(int32_t action) { - switch (MotionEvent::getActionMasked(action)) { - case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - case AMOTION_EVENT_ACTION_HOVER_EXIT: { - return true; - } - } - return false; -} -} // namespace - InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {} InputState::~InputState() {} @@ -113,13 +100,6 @@ bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t f if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) && !isStylusEvent(entry.source, entry.pointerProperties)) { // We already have a stylus stream, and the new event is not from stylus. - if (!lastMemento.hovering) { - // If stylus is currently down, reject the new event unconditionally. - return false; - } - } - if (!lastMemento.hovering && isHoverAction(action)) { - // Reject hovers if already down return false; } } @@ -366,19 +346,13 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry, return false; } - // We want stylus down to block touch and other source types, but stylus hover should not - // have such an effect. - if (isHoverAction(motionEntry.action) && !lastMemento.hovering) { - // New event is a hover. Keep the current non-hovering gesture instead - return false; - } - - if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) && !lastMemento.hovering) { - // We have non-hovering stylus already active. + if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties)) { + // A stylus is already active. if (isStylusEvent(motionEntry.source, motionEntry.pointerProperties) && actionMasked == AMOTION_EVENT_ACTION_DOWN) { - // If this new event is a stylus from a different device going down, then cancel the old - // stylus and allow the new stylus to take over + // If this new event is from a different device, then cancel the old + // stylus and allow the new stylus to take over, but only if it's going down. + // Otherwise, they will start to race each other. return true; } @@ -421,9 +395,10 @@ std::unique_ptr<MotionEntry> InputState::createCancelEntryForMemento(const Motio 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 +412,10 @@ std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents 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 +474,8 @@ std::vector<std::unique_ptr<EventEntry>> InputState::synthesizePointerDownEvents | (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 +515,11 @@ std::vector<std::unique_ptr<MotionEntry>> InputState::synthesizeCancelationEvent 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 +540,8 @@ std::vector<std::unique_ptr<MotionEntry>> InputState::synthesizeCancelationEvent : 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/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index d099b44d91..bc7b6445ff 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -221,7 +221,8 @@ public: /* * Updates key repeat configuration timeout and delay. */ - virtual void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) = 0; + virtual void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, + std::chrono::nanoseconds delay) = 0; }; } // namespace android diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index bf4880480c..1c23720ecb 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 @@ public: 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/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 25e1d21850..efc8b260f3 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -451,6 +451,15 @@ public: /* Returns true if any InputConnection is currently active. */ virtual bool isInputMethodConnectionActive() = 0; + + /* Gets the viewport of a particular display that the pointer device is associated with. If + * the pointer device is not associated with any display, it should ADISPLAY_IS_NONE to get + * the viewport that should be used. The device should get a new viewport using this method + * every time there is a display configuration change. The logical bounds of the viewport should + * be used as the range of possible values for pointing devices, like mice and touchpads. + */ + virtual std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0; }; } // namespace android diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h index 9e020c7c7f..8b47b555e5 100644 --- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h +++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h @@ -38,7 +38,16 @@ public: * library, libinputservice, that has the additional dependencies. The PointerController * will be mocked when testing PointerChoreographer. */ - virtual std::shared_ptr<PointerControllerInterface> createPointerController() = 0; + virtual std::shared_ptr<PointerControllerInterface> createPointerController( + PointerControllerInterface::ControllerType type) = 0; + + /** + * Notifies the policy that the default pointer displayId has changed. PointerChoreographer is + * the single source of truth for all pointers on screen. + * @param displayId The updated display on which the mouse cursor is shown + * @param position The new position of the mouse cursor on the logical display + */ + virtual void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) = 0; }; } // namespace android diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 95f819a8da..ef74a55860 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -48,10 +48,30 @@ struct FloatPoint { */ class PointerControllerInterface { protected: - PointerControllerInterface() { } - virtual ~PointerControllerInterface() { } + PointerControllerInterface() {} + virtual ~PointerControllerInterface() {} public: + /** + * Enum used to differentiate various types of PointerControllers for the transition to + * using PointerChoreographer. + * + * TODO(b/293587049): Refactor the PointerController class into different controller types. + */ + enum class ControllerType { + // The PointerController that is responsible for drawing all icons. + 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. */ + virtual std::string dump() = 0; + /* Gets the bounds of the region that the pointer can traverse. * Returns true if the bounds are available. */ virtual std::optional<FloatRect> getBounds() const = 0; @@ -105,7 +125,7 @@ public: * 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/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 5766b14231..0582649d38 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -1046,6 +1046,14 @@ bool InputReader::ContextImpl::isPreventingTouchpadTaps() { return mReader->mPreventingTouchpadTaps; } +void InputReader::ContextImpl::setLastKeyDownTimestamp(nsecs_t when) { + mReader->mLastKeyDownTimestamp = when; +} + +nsecs_t InputReader::ContextImpl::getLastKeyDownTimestamp() { + return mReader->mLastKeyDownTimestamp; +} + void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) { // lock is already held by the input loop mReader->disableVirtualKeysUntilLocked(time); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 9a297c9ace..4c78db38cc 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -158,6 +158,9 @@ protected: void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock) REQUIRES(mLock) override; bool isPreventingTouchpadTaps() REQUIRES(mReader->mLock) REQUIRES(mLock) override; + void setLastKeyDownTimestamp(nsecs_t when) REQUIRES(mReader->mLock) + REQUIRES(mLock) override; + nsecs_t getLastKeyDownTimestamp() REQUIRES(mReader->mLock) REQUIRES(mLock) override; } mContext; friend class ContextImpl; @@ -198,6 +201,9 @@ private: // true if tap-to-click on touchpad currently disabled bool mPreventingTouchpadTaps GUARDED_BY(mLock){false}; + // records timestamp of the last key press on the physical keyboard + nsecs_t mLastKeyDownTimestamp GUARDED_BY(mLock){0}; + // low-level input event decoding and device management [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index aed75636f7..69b2315a6c 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -65,6 +65,9 @@ public: virtual void setPreventingTouchpadTaps(bool prevent) = 0; virtual bool isPreventingTouchpadTaps() = 0; + + virtual void setLastKeyDownTimestamp(nsecs_t when) = 0; + virtual nsecs_t getLastKeyDownTimestamp() = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 79f07a5f7c..7aeb215174 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -20,6 +20,7 @@ #include "CursorInputMapper.h" +#include <com_android_input_flags.h> #include <optional> #include "CursorButtonAccumulator.h" @@ -29,6 +30,8 @@ #include "input/PrintTools.h" +namespace input_flags = com::android::input::flags; + namespace android { // The default velocity control parameters that has no effect. @@ -71,7 +74,8 @@ void CursorMotionAccumulator::finishSync() { CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig) : InputMapper(deviceContext, readerConfig), - mLastEventTime(std::numeric_limits<nsecs_t>::min()) {} + mLastEventTime(std::numeric_limits<nsecs_t>::min()), + mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {} CursorInputMapper::~CursorInputMapper() { if (mPointerController != nullptr) { @@ -87,11 +91,11 @@ void CursorInputMapper::populateDeviceInfo(InputDeviceInfo& info) { InputMapper::populateDeviceInfo(info); if (mParameters.mode == Parameters::Mode::POINTER) { - if (const auto bounds = mPointerController->getBounds(); bounds) { - info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, bounds->left, bounds->right, 0.0f, - 0.0f, 0.0f); - info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, bounds->top, bounds->bottom, 0.0f, - 0.0f, 0.0f); + if (!mBoundsInLogicalDisplay.isEmpty()) { + info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, mBoundsInLogicalDisplay.left, + mBoundsInLogicalDisplay.right, 0.0f, 0.0f, 0.0f); + info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, mBoundsInLogicalDisplay.top, + mBoundsInLogicalDisplay.bottom, 0.0f, 0.0f, 0.0f); } } else { info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); @@ -283,19 +287,22 @@ std::list<NotifyArgs> CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mSource == AINPUT_SOURCE_MOUSE) { - if (moved || scrolled || buttonsChanged) { - mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); - - if (moved) { - mPointerController->move(deltaX, deltaY); + if (!mEnablePointerChoreographer) { + if (moved || scrolled || buttonsChanged) { + mPointerController->setPresentation( + PointerControllerInterface::Presentation::POINTER); + + if (moved) { + mPointerController->move(deltaX, deltaY); + } + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } - mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); - } - std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition(); + std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + } pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); } else { @@ -499,31 +506,55 @@ void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfigurat const bool isPointer = mParameters.mode == Parameters::Mode::POINTER; mDisplayId = ADISPLAY_ID_NONE; - if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) { + std::optional<DisplayViewport> resolvedViewport; + bool isBoundsSet = false; + if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) { // This InputDevice is associated with a viewport. // Only generate events for the associated display. - const bool mismatchedPointerDisplay = - isPointer && (viewport->displayId != mPointerController->getDisplayId()); - mDisplayId = - mismatchedPointerDisplay ? std::nullopt : std::make_optional(viewport->displayId); + mDisplayId = assocViewport->displayId; + resolvedViewport = *assocViewport; + if (!mEnablePointerChoreographer) { + const bool mismatchedPointerDisplay = + isPointer && (assocViewport->displayId != mPointerController->getDisplayId()); + if (mismatchedPointerDisplay) { + // This device's associated display doesn't match PointerController's current + // display. Do not associate it with any display. + mDisplayId.reset(); + } + } } else if (isPointer) { // The InputDevice is not associated with a viewport, but it controls the mouse pointer. - mDisplayId = mPointerController->getDisplayId(); + if (mEnablePointerChoreographer) { + // Always use DISPLAY_ID_NONE for mouse events. + // PointerChoreographer will make it target the correct the displayId later. + const auto pointerViewport = + getContext()->getPolicy()->getPointerViewportForAssociatedDisplay(); + mDisplayId = pointerViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt; + resolvedViewport = pointerViewport; + } else { + mDisplayId = mPointerController->getDisplayId(); + if (auto v = config.getDisplayViewportById(*mDisplayId); v) { + resolvedViewport = *v; + } + if (auto bounds = mPointerController->getBounds(); bounds) { + mBoundsInLogicalDisplay = *bounds; + isBoundsSet = true; + } + } } - mOrientation = ui::ROTATION_0; - const bool isOrientedDevice = - (mParameters.orientationAware && mParameters.hasAssociatedDisplay); - // InputReader works in the un-rotated display coordinate space, so we don't need to do - // anything if the device is already orientation-aware. If the device is not - // orientation-aware, then we need to apply the inverse rotation of the display so that - // when the display rotation is applied later as a part of the per-window transform, we - // get the expected screen coordinates. When pointer capture is enabled, we do not apply any - // rotations and report values directly from the input device. - if (!isOrientedDevice && mDisplayId && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) { - if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) { - mOrientation = getInverseRotation(viewport->orientation); - } + mOrientation = (mParameters.orientationAware && mParameters.hasAssociatedDisplay) || + mParameters.mode == Parameters::Mode::POINTER_RELATIVE || !resolvedViewport + ? ui::ROTATION_0 + : getInverseRotation(resolvedViewport->orientation); + + if (!isBoundsSet) { + mBoundsInLogicalDisplay = resolvedViewport + ? FloatRect{static_cast<float>(resolvedViewport->logicalLeft), + static_cast<float>(resolvedViewport->logicalTop), + static_cast<float>(resolvedViewport->logicalRight - 1), + static_cast<float>(resolvedViewport->logicalBottom - 1)} + : FloatRect{0, 0, 0, 0}; } bumpGeneration(); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index b879bfdd08..308adaa463 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -119,7 +119,8 @@ private: // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e. // std::nullopt), all events will be ignored. std::optional<int32_t> mDisplayId; - ui::Rotation mOrientation; + ui::Rotation mOrientation{ui::ROTATION_0}; + FloatRect mBoundsInLogicalDisplay{}; std::shared_ptr<PointerControllerInterface> mPointerController; @@ -127,6 +128,8 @@ private: nsecs_t mDownTime; nsecs_t mLastEventTime; + const bool mEnablePointerChoreographer; + explicit CursorInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig); void dumpParameters(std::string& dump); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 531fc6766d..f068cc8aa4 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -270,7 +270,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read keyDown.flags = flags; mKeyDowns.push_back(keyDown); } - onKeyDownProcessed(); + onKeyDownProcessed(downTime); } else { // Remove key down. if (keyDownIndex) { @@ -448,8 +448,9 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { return out; } -void KeyboardInputMapper::onKeyDownProcessed() { +void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) { InputReaderContext& context = *getContext(); + context.setLastKeyDownTimestamp(downTime); if (context.isPreventingTouchpadTaps()) { // avoid pinging java service unnecessarily, just fade pointer again if it became visible context.fadePointer(); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 09808df18c..500256b21f 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -107,7 +107,7 @@ private: void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig); [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when); - void onKeyDownProcessed(); + void onKeyDownProcessed(nsecs_t downTime); }; } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp index f70be72741..b0fc9035f2 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -152,6 +152,14 @@ void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Sl } } +size_t MultiTouchMotionAccumulator::getActiveSlotsCount() const { + if (!mUsingSlotsProtocol) { + return mCurrentSlot < 0 ? 0 : mCurrentSlot; + } + return std::count_if(mSlots.begin(), mSlots.end(), + [](const Slot& slot) { return slot.mInUse; }); +} + // --- MultiTouchMotionAccumulator::Slot --- ToolType MultiTouchMotionAccumulator::Slot::getToolType() const { diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h index 943dde5ca2..0e3e2bb365 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -77,6 +77,7 @@ public: void process(const RawEvent* rawEvent); void finishSync(); + size_t getActiveSlotsCount() const; inline size_t getSlotCount() const { return mSlots.size(); } inline const Slot& getSlot(size_t index) const { LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 64e8825f34..db31dede2f 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -58,6 +58,7 @@ cc_test { "InputReader_test.cpp", "InstrumentedInputReader.cpp", "LatencyTracker_test.cpp", + "MultiTouchMotionAccumulator_test.cpp", "NotifyArgs_test.cpp", "PointerChoreographer_test.cpp", "PreferStylusOverTouch_test.cpp", diff --git a/services/inputflinger/tests/FakeApplicationHandle.h b/services/inputflinger/tests/FakeApplicationHandle.h new file mode 100644 index 0000000000..2f634d58ea --- /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 0000000000..e9d93af67b --- /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/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 41c98ef9e9..88f514f1d4 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -261,4 +261,17 @@ void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t mStylusGestureNotified = deviceId; } +std::optional<DisplayViewport> FakeInputReaderPolicy::getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId) { + if (associatedDisplayId == ADISPLAY_ID_NONE) { + associatedDisplayId = mConfig.defaultPointerDisplayId; + } + for (auto& viewport : mViewports) { + if (viewport.displayId == associatedDisplayId) { + return std::make_optional(viewport); + } + } + return std::nullopt; +} + } // namespace android diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 48912a6a28..4ef9c3ee4a 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -79,6 +79,8 @@ public: void setStylusPointerIconEnabled(bool enabled); void setIsInputMethodConnectionActive(bool active); bool isInputMethodConnectionActive() override; + std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId) override; private: void getReaderConfiguration(InputReaderConfiguration* outConfig) override; diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp index ca517f32c9..5475594113 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -47,6 +47,8 @@ int32_t FakePointerController::getDisplayId() const { 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 @@ void FakePointerController::assertPosition(float x, float y) { 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 c374267ff9..d7e40b3cc1 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -37,9 +37,11 @@ public: void setDisplayViewport(const DisplayViewport& viewport) override; void assertPosition(float x, float y); + void assertSpotCount(int32_t displayId, int32_t count); bool isPointerShown(); private: + std::string dump() override { return ""; } std::optional<FloatRect> getBounds() const override; void move(float deltaX, float deltaY) override; void fade(Transition) override; @@ -52,7 +54,7 @@ private: bool mHaveBounds{false}; float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0}; float mX{0}, mY{0}; - int32_t mDisplayId{ADISPLAY_ID_DEFAULT}; + int32_t mDisplayId{ADISPLAY_ID_NONE}; bool mIsPointerShown{false}; std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h new file mode 100644 index 0000000000..fe25130bd8 --- /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 606dd6d14f..71362e3be9 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::millisecon 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 @@ public: 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 @@ public: /** * 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 @@ public: << ": 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 @@ public: 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 @@ public: 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 @@ std::atomic<int32_t> FakeWindowHandle::sId{1}; 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 @@ public: 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( @@ -2167,6 +2176,69 @@ TEST_F(InputDispatcherTest, TwoPointerCancelInconsistentPolicy) { } /** + * Same as the above 'TwoPointerCancelInconsistentPolicy' test, but for hovers. + * The policy typically sets POLICY_FLAG_PASS_TO_USER to the events. But when the display is not + * interactive, it might stop sending this flag. + * We've already ensured the consistency of the touch event in this case, and we should also ensure + * the consistency of the hover event in this case. + * + * Test procedure: + * HOVER_ENTER -> HOVER_MOVE -> (stop sending POLICY_FLAG_PASS_TO_USER) -> HOVER_EXIT + * HOVER_ENTER -> HOVER_MOVE -> HOVER_EXIT + * + * We expect to receive two full streams of hover events. + */ +TEST_F(InputDispatcherTest, HoverEventInconsistentPolicy) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 300, 300)); + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(101)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE)); + + // Send hover exit without the default policy flags. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .policyFlags(0) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102)) + .build()); + + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + + // Send a simple hover event stream, ensure dispatcher not crashed and window can receive + // right event. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(200).y(201)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(201).y(202)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE)); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(201).y(202)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); +} + +/** * Two windows: a window on the left and a window on the right. * Mouse is hovered from the right window into the left window. * Next, we tap on the left window, where the cursor was last seen. @@ -2521,9 +2593,9 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyBlocksTouchDown) { /** * One window. Stylus hover on the window. Next, touch from another device goes down. Ensure that - * touch is not dropped, because stylus hover should be ignored. + * touch is dropped, because stylus hover takes precedence. */ -TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) { +TEST_F(InputDispatcherMultiDeviceTest, StylusHoverBlocksTouchDown) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); @@ -2552,34 +2624,29 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) { .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146)) .build()); - // Stylus hover is canceled because touch is down - window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), - WithDeviceId(stylusDeviceId), WithCoords(100, 110))); - window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId), - WithCoords(140, 145))); - window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId), - WithCoords(141, 146))); + // Touch is ignored because stylus is hovering - // Subsequent stylus movements are ignored + // Subsequent stylus movements are delivered correctly mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) .deviceId(stylusDeviceId) .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111)) .build()); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), + WithDeviceId(stylusDeviceId), WithCoords(101, 111))); - // but subsequent touches continue to be delivered + // and subsequent touches continue to be ignored mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) .deviceId(touchDeviceId) .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147)) .build()); - window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId), - WithCoords(142, 147))); + window->assertNoEvents(); } /** * One window. Touch down on the window. Then, stylus hover on the window from another device. - * Ensure that touch is not canceled, because stylus hover should be dropped. + * Ensure that touch is canceled, because stylus hover should take precedence. */ -TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) { +TEST_F(InputDispatcherMultiDeviceTest, TouchIsCanceledByStylusHover) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); @@ -2611,15 +2678,21 @@ TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) { .deviceId(stylusDeviceId) .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111)) .build()); - // Stylus hover movement is dropped + // Stylus hover movement causes touch to be canceled + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId), + WithCoords(141, 146))); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER), + WithDeviceId(stylusDeviceId), WithCoords(100, 110))); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), + WithDeviceId(stylusDeviceId), WithCoords(101, 111))); + // Subsequent touch movements are ignored mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) .deviceId(touchDeviceId) .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147)) .build()); - // Subsequent touch movements are delivered correctly - window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId), - WithCoords(142, 147))); + + window->assertNoEvents(); } /** @@ -2936,11 +3009,11 @@ TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceWithSpy) { * Three windows: a window on the left, a window on the right, and a spy window positioned above * both. * Check hover in left window and touch down in the right window. - * At first, spy should receive hover, but the touch down should cancel hovering inside spy. + * At first, spy should receive hover. Spy shouldn't receive touch while stylus is hovering. * At the same time, left and right should be getting independent streams of hovering and touch, * respectively. */ -TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlockedByTouchWithSpy) { +TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlocksTouchWithSpy) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> spyWindow = @@ -2980,28 +3053,25 @@ TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlockedByTouchWithSpy) { .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100)) .build()); leftWindow->assertNoEvents(); - spyWindow->consumeMotionEvent( - AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId))); - spyWindow->consumeMotionEvent( - AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); + spyWindow->assertNoEvents(); rightWindow->consumeMotionEvent( AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); - // Stylus movements continue. They should be delivered to the left window only. + // Stylus movements continue. They should be delivered to the left window and the spy. mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) .deviceId(stylusDeviceId) .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110)) .build()); leftWindow->consumeMotionEvent( AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId))); + spyWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId))); - // Touch movements continue. They should be delivered to the right window and to the spy + // Touch movements continue. They should be delivered to the right window only mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) .deviceId(touchDeviceId) .pointer(PointerBuilder(0, ToolType::FINGER).x(301).y(101)) .build()); - spyWindow->consumeMotionEvent( - AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId))); rightWindow->consumeMotionEvent( AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId))); @@ -3216,7 +3286,7 @@ TEST_F(InputDispatcherMultiDeviceTest, HoverTapAndSplitTouch) { * While the touch is down, new hover events from the stylus device should be ignored. After the * touch is gone, stylus hovering should start working again. */ -TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { +TEST_F(InputDispatcherMultiDeviceTest, StylusHoverIgnoresTouchTap) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); @@ -3242,10 +3312,7 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { .deviceId(touchDeviceId) .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100)) .build())); - // The touch device should cause hover to stop! - window->consumeMotionEvent( - AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId))); - window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); + // The touch device should be ignored! // Continue hovering with stylus. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -3255,7 +3322,9 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { .deviceId(stylusDeviceId) .pointer(PointerBuilder(0, ToolType::STYLUS).x(60).y(60)) .build())); - // Hovers are now ignored + // Hovers continue to work + window->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId))); // Lift up the finger ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -3265,7 +3334,6 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { .deviceId(touchDeviceId) .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100)) .build())); - window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(touchDeviceId))); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, @@ -3274,8 +3342,8 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) { .deviceId(stylusDeviceId) .pointer(PointerBuilder(0, ToolType::STYLUS).x(70).y(70)) .build())); - window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), - WithDeviceId(stylusDeviceId))); + window->consumeMotionEvent( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId))); window->assertNoEvents(); } @@ -6471,10 +6539,217 @@ TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithKeys) { 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 - static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000; // 40 ms + static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms; + static constexpr std::chrono::nanoseconds KEY_REPEAT_DELAY = 40ms; std::shared_ptr<FakeApplicationHandle> mApp; sp<FakeWindowHandle> mWindow; @@ -7856,7 +8131,8 @@ TEST_F(InputDispatcherSingleWindowAnr, // Injection is async, so it will succeed ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, - ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE)); + ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT, + /*allowKeyRepeat=*/false)); // At this point, key is still pending, and should not be sent to the application yet. // Make sure the `assertNoEvents` check doesn't take too long. It uses // CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood. diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index dac4ea02c4..787444c9e6 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -80,9 +80,15 @@ void InputMapperUnitTest::setKeyCodeState(KeyState state, std::set<int> keyCodes } std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, int32_t value) { + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return process(when, type, code, value); +} + +std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, int32_t type, int32_t code, + int32_t value) { RawEvent event; - event.when = systemTime(SYSTEM_TIME_MONOTONIC); - event.readTime = event.when; + event.when = when; + event.readTime = when; event.deviceId = mMapper->getDeviceContext().getEventHubId(); event.type = type; event.code = code; diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index c2ac258765..3f9061fc6a 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -52,6 +52,7 @@ protected: void setKeyCodeState(KeyState state, std::set<int> keyCodes); std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value); + std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value); MockEventHubInterface mMockEventHub; std::shared_ptr<FakePointerController> mFakePointerController; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 64ae9e8b14..5044b3e777 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -37,6 +37,7 @@ #include <UinputDevice.h> #include <VibratorInputMapper.h> #include <android-base/thread_annotations.h> +#include <com_android_input_flags.h> #include <ftl/enum.h> #include <gtest/gtest.h> #include <gui/constants.h> @@ -99,6 +100,8 @@ static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); // Maximum smoothing time delta so that we don't generate events too far into the future. constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); +namespace input_flags = com::android::input::flags; + template<typename T> static inline T min(T a, T b) { return a < b ? a : b; @@ -4097,9 +4100,9 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } -// --- CursorInputMapperTest --- +// --- CursorInputMapperTestBase --- -class CursorInputMapperTest : public InputMapperTest { +class CursorInputMapperTestBase : public InputMapperTest { protected: static const int32_t TRACKBALL_MOVEMENT_THRESHOLD; @@ -4133,11 +4136,11 @@ protected: } }; -const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6; +const int32_t CursorInputMapperTestBase::TRACKBALL_MOVEMENT_THRESHOLD = 6; -void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_t originalX, - int32_t originalY, int32_t rotatedX, - int32_t rotatedY) { +void CursorInputMapperTestBase::testMotionRotation(CursorInputMapper& mapper, int32_t originalX, + int32_t originalY, int32_t rotatedX, + int32_t rotatedY) { NotifyMotionArgs args; process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, originalX); @@ -4151,6 +4154,16 @@ void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_ float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f)); } +// --- CursorInputMapperTest --- + +class CursorInputMapperTest : public CursorInputMapperTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(false); + CursorInputMapperTestBase::SetUp(); + } +}; + TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) { addConfigurationProperty("cursor.mode", "pointer"); CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); @@ -4180,6 +4193,7 @@ TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeF // When the bounds are set, then there should be a valid motion range. mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); InputDeviceInfo info2; mapper.populateDeviceInfo(info2); @@ -4906,11 +4920,459 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPoint ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } +// --- CursorInputMapperTestWithChoreographer --- + +class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(true); + CursorInputMapperTestBase::SetUp(); + } +}; + +TEST_F(CursorInputMapperTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + InputDeviceInfo info; + mapper.populateDeviceInfo(info); + + // Initially there may not be a valid motion range. + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); + + // When the viewport and the default pointer display ID is set, then there should be a valid + // motion range. + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + InputDeviceInfo info2; + mapper.populateDeviceInfo(info2); + + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0, + DISPLAY_WIDTH - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0, + DISPLAY_HEIGHT - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + prepareDisplay(ui::ROTATION_0); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + NotifyMotionArgs motionArgs; + NotifyKeyArgs keyArgs; + + // press BTN_LEFT, release BTN_LEFT + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, + motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, + motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + // press BTN_BACK, release BTN_BACK + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); + + // press BTN_SIDE, release BTN_SIDE + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); + + // press BTN_FORWARD, release BTN_FORWARD + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); + + // press BTN_EXTRA, release BTN_EXTRA + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); +} + +TEST_F(CursorInputMapperTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + prepareDisplay(ui::ROTATION_0); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + NotifyMotionArgs args; + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) { + addConfigurationProperty("cursor.mode", "pointer"); + const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, + /*highThreshold=*/100.f, /*acceleration=*/10.f); + mFakePolicy->setVelocityControlParams(testParams); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + prepareDisplay(ui::ROTATION_0); + + NotifyDeviceResetArgs resetArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); + ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); + + NotifyMotionArgs args; + + // Move and verify scale is applied. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); + const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_GT(relX, 10); + ASSERT_GT(relY, 20); + + // Enable Pointer Capture + mFakePolicy->setPointerCapture(true); + configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); + NotifyPointerCaptureChangedArgs captureArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); + ASSERT_TRUE(captureArgs.request.enable); + + // Move and verify scale is not applied. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); + const float relX2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_EQ(10, relX2); + ASSERT_EQ(20, relY2); +} + +TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) { + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_90); + + // Set up the secondary display as the display on which the pointer should be shown. + // The InputDevice is not associated with any display. + prepareSecondaryDisplay(); + mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); + mFakePointerController->setPosition(100, 200); + + // Ensure input events are generated without display ID and coords, + // because they will be decided later by PointerChoreographer. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE), + WithCoords(0.0f, 0.0f)))); +} + +TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) { + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_90); + + // Set up the secondary display as the display on which the pointer should be shown, + // and associate the InputDevice with the secondary display. + prepareSecondaryDisplay(); + mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); + mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); + mFakePointerController->setPosition(100, 200); + + // Ensure input events are generated with associated display ID but not with coords, + // because the coords will be decided later by PointerChoreographer. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), + WithCoords(0.0f, 0.0f)))); +} + +TEST_F(CursorInputMapperTestWithChoreographer, + ConfigureDisplayIdShouldGenerateEventWithMismatchedPointerDisplay) { + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display as the display on which the pointer should be shown. + prepareDisplay(ui::ROTATION_90); + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + + // Associate the InputDevice with the secondary display. + prepareSecondaryDisplay(); + mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + // With PointerChoreographer enabled, there could be a PointerController for the associated + // display even if it is different from the pointer display. So the mapper should generate an + // event. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), + WithCoords(0.0f, 0.0f)))); +} + // --- BluetoothCursorInputMapperTest --- -class BluetoothCursorInputMapperTest : public CursorInputMapperTest { +class BluetoothCursorInputMapperTest : public CursorInputMapperTestBase { protected: void SetUp() override { + input_flags::enable_pointer_choreographer(false); InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); mFakePointerController = std::make_shared<FakePointerController>(); @@ -5006,6 +5468,122 @@ TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) { WithEventTime(expectedEventTime)))); } +// --- BluetoothCursorInputMapperTestWithChoreographer --- + +class BluetoothCursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(true); + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); + + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + } +}; + +TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmoothening) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_0); + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + } +} + +TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningIsCapped) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_0); + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // Process several events with the same timestamp from the kernel. + // Ensure that we do not generate events too far into the future. + constexpr static int32_t numEvents = + MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; + for (int i = 0; i < numEvents; i++) { + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + } + + // By processing more events with the same timestamp, we should not generate events with a + // timestamp that is more than the specified max time delta from the timestamp at its injection. + const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; + for (int i = 0; i < 3; i++) { + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(cappedEventTime)))); + } +} + +TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningNotUsed) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_0); + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp + // smoothening is not needed, its timestamp is not affected. + kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); + expectedEventTime = kernelEventTime; + + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); +} + // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h index ca85558a0c..e9c7bb44e8 100644 --- a/services/inputflinger/tests/InstrumentedInputReader.h +++ b/services/inputflinger/tests/InstrumentedInputReader.h @@ -106,6 +106,9 @@ protected: void setPreventingTouchpadTaps(bool prevent) override { mPreventingTouchpadTaps = prevent; } bool isPreventingTouchpadTaps() override { return mPreventingTouchpadTaps; } + void setLastKeyDownTimestamp(nsecs_t when) override { mLastKeyDownTimestamp = when; }; + nsecs_t getLastKeyDownTimestamp() override { return mLastKeyDownTimestamp; }; + private: int32_t mGlobalMetaState; bool mUpdateGlobalMetaStateWasCalled; @@ -113,6 +116,7 @@ protected: std::optional<nsecs_t> mRequestedTimeout; std::vector<InputDeviceInfo> mExternalStylusDevices; bool mPreventingTouchpadTaps{false}; + nsecs_t mLastKeyDownTimestamp; } mFakeContext; friend class InputReaderTest; diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 05823cd9de..73949136f4 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -77,6 +77,9 @@ public: MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override)); MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override)); + MOCK_METHOD(void, setLastKeyDownTimestamp, (nsecs_t when)); + MOCK_METHOD(nsecs_t, getLastKeyDownTimestamp, ()); + private: int32_t mGeneration = 0; }; diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index 48f5673c51..2ef79999a2 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -26,6 +26,7 @@ namespace android { using testing::_; +using testing::Args; using testing::DoAll; using testing::Return; using testing::SetArgPointee; @@ -158,4 +159,18 @@ TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTo testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false); } +TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) { + nsecs_t when = ARBITRARY_TIME; + std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT}; + EXPECT_CALL(mMockInputReaderContext, setLastKeyDownTimestamp) + .With(Args<0>(when)) + .Times(keyCodes.size()); + for (int32_t keyCode : keyCodes) { + process(when, EV_KEY, keyCode, 1); + process(when, EV_SYN, SYN_REPORT, 0); + process(when, EV_KEY, keyCode, 0); + process(when, EV_SYN, SYN_REPORT, 0); + } +} + } // namespace android diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp new file mode 100644 index 0000000000..9fa6cdd298 --- /dev/null +++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp @@ -0,0 +1,87 @@ +/* + * 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 "MultiTouchMotionAccumulator.h" +#include "InputMapperTest.h" + +namespace android { + +class MultiTouchMotionAccumulatorTest : public InputMapperUnitTest { +protected: + static constexpr size_t SLOT_COUNT = 8; + + MultiTouchMotionAccumulator mMotionAccumulator; + + void processMotionEvent(int32_t type, int32_t code, int32_t value) { + RawEvent event; + event.when = ARBITRARY_TIME; + event.readTime = READ_TIME; + event.deviceId = EVENTHUB_ID; + event.type = type; + event.code = code; + event.value = value; + mMotionAccumulator.process(&event); + } +}; + +TEST_F(MultiTouchMotionAccumulatorTest, ActiveSlotCountUsingSlotsProtocol) { + mMotionAccumulator.configure(*mDeviceContext, SLOT_COUNT, /*usingSlotsProtocol=*/true); + // We expect active slot count to match the touches being tracked + // first touch + processMotionEvent(EV_ABS, ABS_MT_SLOT, 0); + processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, 123); + processMotionEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount()); + + // second touch + processMotionEvent(EV_ABS, ABS_MT_SLOT, 1); + processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, 456); + processMotionEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(2u, mMotionAccumulator.getActiveSlotsCount()); + + // second lifted + processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); + processMotionEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount()); + + // first lifted + processMotionEvent(EV_ABS, ABS_MT_SLOT, 0); + processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); + processMotionEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(0u, mMotionAccumulator.getActiveSlotsCount()); +} + +TEST_F(MultiTouchMotionAccumulatorTest, ActiveSlotCountNotUsingSlotsProtocol) { + mMotionAccumulator.configure(*mDeviceContext, SLOT_COUNT, /*usingSlotsProtocol=*/false); + + // first touch + processMotionEvent(EV_ABS, ABS_MT_POSITION_X, 0); + processMotionEvent(EV_ABS, ABS_MT_POSITION_Y, 0); + processMotionEvent(EV_SYN, SYN_MT_REPORT, 0); + ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount()); + + // second touch + processMotionEvent(EV_ABS, ABS_MT_POSITION_X, 50); + processMotionEvent(EV_ABS, ABS_MT_POSITION_Y, 50); + processMotionEvent(EV_SYN, SYN_MT_REPORT, 0); + ASSERT_EQ(2u, mMotionAccumulator.getActiveSlotsCount()); + + // reset + mMotionAccumulator.finishSync(); + ASSERT_EQ(0u, mMotionAccumulator.getActiveSlotsCount()); +} + +} // namespace android diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 7237424e2a..68f58571f5 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -19,16 +19,63 @@ #include <gtest/gtest.h> #include <vector> +#include "FakePointerController.h" +#include "NotifyArgsBuilders.h" +#include "TestEventMatchers.h" #include "TestInputListener.h" namespace android { +using ControllerType = PointerControllerInterface::ControllerType; +using testing::AllOf; + +namespace { + // Helpers to std::visit with lambdas. template <typename... V> struct Visitor : V... {}; template <typename... V> 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) { + InputDeviceIdentifier identifier; + + auto info = InputDeviceInfo(); + info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias", + /*isExternal=*/false, /*hasMic=*/false, associatedDisplayId); + info.addSource(source); + return info; +} + +static std::vector<DisplayViewport> createViewports(std::vector<int32_t> displayIds) { + std::vector<DisplayViewport> viewports; + 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 { @@ -36,7 +83,55 @@ protected: TestInputListener mTestListener; PointerChoreographer mChoreographer{mTestListener, *this}; - std::shared_ptr<PointerControllerInterface> createPointerController() { return {}; } + 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 assertPointerControllerNotCreated() { ASSERT_EQ(std::nullopt, mLastCreatedController); } + + 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) { + ASSERT_EQ(displayId, mPointerDisplayIdNotified); + mPointerDisplayIdNotified.reset(); + } + + void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); } + +private: + 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) { @@ -86,4 +181,873 @@ TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { } } +TEST_F(PointerChoreographerTest, WhenMouseIsJustAddedDoesNotCreatePointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + assertPointerControllerNotCreated(); +} + +TEST_F(PointerChoreographerTest, WhenMouseEventOccursCreatesPointerController) { + 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()); + assertPointerControllerCreated(ControllerType::MOUSE); +} + +TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) { + 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()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + + // Remove the mouse. + mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}}); + assertPointerControllerRemoved(pc); +} + +TEST_F(PointerChoreographerTest, WhenKeyboardIsAddedDoesNotCreatePointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE)}}); + assertPointerControllerNotCreated(); +} + +TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) { + // Just adding a viewport or device should not create a PointerController. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + 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.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); +} + +TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) { + // Without viewport information, PointerController will be created by a mouse event + // but viewport won't 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()); + 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, pc->getDisplayId()); +} + +TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // For a mouse event without a target display, default viewport should be set for + // the PointerController. + 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()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); +} + +TEST_F(PointerChoreographerTest, + WhenDefaultMouseDisplayChangesSetsDefaultMouseViewportForPointerController) { + // Set one display as a default mouse display and emit mouse event to create PointerController. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_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()); + 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(); + + // New PointerController for the new default display will be created by the motion event. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(ANOTHER_DISPLAY_ID, secondDisplayPc->getDisplayId()); +} + +TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) { + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.setDisplayViewports(createViewports({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()); + assertPointerControllerCreated(ControllerType::MOUSE); + + assertPointerDisplayIdNotified(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) { + 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()); + assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotNotified(); + + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + assertPointerDisplayIdNotified(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdChanged) { + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.setDisplayViewports(createViewports({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()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotified(DISPLAY_ID); + + mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}}); + assertPointerDisplayIdNotified(ADISPLAY_ID_NONE); + assertPointerControllerRemoved(pc); +} + +TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointerDisplayIdChanged) { + // Add two viewports. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + + // Set one viewport as a default mouse 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()); + 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( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .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 ee6ff53d8c..a0a0f5a3de 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -464,9 +464,32 @@ inline WithPointersMatcher WithPointers( 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 9313a89005..8a4f6f0acc 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -167,3 +167,17 @@ cc_fuzz { "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 0000000000..885820fafb --- /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 3b3ed9ba0d..deb811d1ca 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 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { 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 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { }, [&]() -> void { // SendToNextStage_NotifyMotionArgs - mClassifier->notifyMotion(generateFuzzedMotionArgs(fdp)); + mClassifier->notifyMotion( + generateFuzzedMotionArgs(idGenerator, fdp, MAX_RANDOM_DISPLAYS)); }, [&]() -> void { // SendToNextStage_NotifySwitchArgs @@ -108,7 +82,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { }, [&]() -> 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 0000000000..214649cf41 --- /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/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index bdedfdfa0c..2f844977ec 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -275,6 +275,7 @@ public: void clearSpots() override {} int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); } void setDisplayViewport(const DisplayViewport& displayViewport) override {} + std::string dump() override { return ""; } }; class FuzzInputReaderPolicy : public InputReaderPolicyInterface { @@ -309,6 +310,10 @@ public: void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; } void notifyStylusGestureStarted(int32_t, nsecs_t) {} bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); } + std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId) override { + return {}; + } }; class FuzzInputListener : public virtual InputListenerInterface { @@ -360,6 +365,12 @@ public: void setPreventingTouchpadTaps(bool prevent) {} bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); }; + + void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; }; + nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; }; + +private: + nsecs_t mLastKeyDownTimestamp; }; template <class Fdp> diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 8b16890a45..1f72e8ba2c 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -19,6 +19,7 @@ cc_library_shared { "PowerHalWrapper.cpp", "PowerSaveState.cpp", "Temperature.cpp", + "WorkDuration.cpp", "WorkSource.cpp", ":libpowermanager_aidl", ], diff --git a/services/powermanager/WorkDuration.cpp b/services/powermanager/WorkDuration.cpp new file mode 100644 index 0000000000..ef723c229c --- /dev/null +++ b/services/powermanager/WorkDuration.cpp @@ -0,0 +1,51 @@ +/** + * 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. + */ + +#define LOG_TAG "WorkDuration" + +#include <android/WorkDuration.h> +#include <android/performance_hint.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +WorkDuration::WorkDuration(int64_t startTimestampNanos, int64_t totalDurationNanos, + int64_t cpuDurationNanos, int64_t gpuDurationNanos) + : workPeriodStartTimestampNanos(startTimestampNanos), + actualTotalDurationNanos(totalDurationNanos), + actualCpuDurationNanos(cpuDurationNanos), + actualGpuDurationNanos(gpuDurationNanos) {} + +status_t WorkDuration::writeToParcel(Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + parcel->writeInt64(workPeriodStartTimestampNanos); + parcel->writeInt64(actualTotalDurationNanos); + parcel->writeInt64(actualCpuDurationNanos); + parcel->writeInt64(actualGpuDurationNanos); + parcel->writeInt64(timestampNanos); + return OK; +} + +status_t WorkDuration::readFromParcel(const Parcel*) { + return INVALID_OPERATION; +} + +} // namespace android::os diff --git a/services/powermanager/include/android/WorkDuration.h b/services/powermanager/include/android/WorkDuration.h new file mode 100644 index 0000000000..99b5b8b1b4 --- /dev/null +++ b/services/powermanager/include/android/WorkDuration.h @@ -0,0 +1,71 @@ +/** + * 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 <binder/Parcelable.h> +#include <math.h> + +struct AWorkDuration {}; + +namespace android::os { + +/** + * C++ Parcelable version of {@link PerformanceHintManager.WorkDuration} that can be used in + * binder calls. + * This file needs to be kept in sync with the WorkDuration in + * frameworks/base/core/java/android/os/WorkDuration.java + */ +struct WorkDuration : AWorkDuration, android::Parcelable { + WorkDuration() = default; + ~WorkDuration() = default; + + WorkDuration(int64_t workPeriodStartTimestampNanos, int64_t actualTotalDurationNanos, + int64_t actualCpuDurationNanos, int64_t actualGpuDurationNanos); + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + + inline bool equalsWithoutTimestamp(const WorkDuration& other) const { + return workPeriodStartTimestampNanos == other.workPeriodStartTimestampNanos && + actualTotalDurationNanos == other.actualTotalDurationNanos && + actualCpuDurationNanos == other.actualCpuDurationNanos && + actualGpuDurationNanos == other.actualGpuDurationNanos; + } + + bool operator==(const WorkDuration& other) const { + return timestampNanos == other.timestampNanos && equalsWithoutTimestamp(other); + } + + bool operator!=(const WorkDuration& other) const { return !(*this == other); } + + friend std::ostream& operator<<(std::ostream& os, const WorkDuration& workDuration) { + os << "{" + << "workPeriodStartTimestampNanos: " << workDuration.workPeriodStartTimestampNanos + << ", actualTotalDurationNanos: " << workDuration.actualTotalDurationNanos + << ", actualCpuDurationNanos: " << workDuration.actualCpuDurationNanos + << ", actualGpuDurationNanos: " << workDuration.actualGpuDurationNanos + << ", timestampNanos: " << workDuration.timestampNanos << "}"; + return os; + } + + int64_t workPeriodStartTimestampNanos; + int64_t actualTotalDurationNanos; + int64_t actualCpuDurationNanos; + int64_t actualGpuDurationNanos; + int64_t timestampNanos; +}; + +} // namespace android::os diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 17fa7bedf7..0989863b7d 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -93,6 +93,7 @@ cc_defaults { "libscheduler", "libserviceutils", "libshaders", + "libsurfaceflinger_common", "libtimestats", "libtonemap", "libsurfaceflingerflags", @@ -175,7 +176,6 @@ filegroup { "FrontEnd/LayerLifecycleManager.cpp", "FrontEnd/RequestedLayerState.cpp", "FrontEnd/TransactionHandler.cpp", - "FlagManager.cpp", "FpsReporter.cpp", "FrameTracer/FrameTracer.cpp", "FrameTracker.cpp", diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 370e4b66e8..2740a979f3 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -63,7 +63,10 @@ cc_defaults { cc_library { name: "libcompositionengine", defaults: ["libcompositionengine_defaults"], - static_libs: ["libsurfaceflingerflags"], + static_libs: [ + "libsurfaceflinger_common", + "libsurfaceflingerflags", + ], srcs: [ "src/planner/CachedSet.cpp", "src/planner/Flattener.cpp", @@ -108,6 +111,7 @@ cc_library { "libgtest", "libgmock", "libcompositionengine", + "libsurfaceflinger_common_test", "libsurfaceflingerflags_test", ], local_include_dirs: ["include"], @@ -143,6 +147,7 @@ cc_test { "librenderengine_mocks", "libgmock", "libgtest", + "libsurfaceflinger_common_test", "libsurfaceflingerflags_test", ], // For some reason, libvulkan isn't picked up from librenderengine diff --git a/services/surfaceflinger/CompositionEngine/AndroidTest.xml b/services/surfaceflinger/CompositionEngine/AndroidTest.xml new file mode 100644 index 0000000000..94e92f0143 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/AndroidTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Config for libcompositionengine_test"> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" + value="libcompositionengine_test->/data/local/tmp/libcompositionengine_test" /> + </target_preparer> + + <!-- + Disable SELinux so that crashes in the test suite produces symbolized stack traces. + --> + <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" /> + + <option name="test-suite-tag" value="apct" /> + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="libcompositionengine_test" /> + </test> +</configuration> diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h index 1a8644e9fe..1a235e9401 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -96,6 +96,9 @@ struct CompositionRefreshArgs { // 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 370c7cf223..40ebf4414d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -298,14 +298,14 @@ protected: 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 6cf1d685b6..de8293151b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -59,7 +59,7 @@ public: 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 229a657236..d95fbeab92 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -102,7 +102,7 @@ public: 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 @@ protected: }; 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 6cb1e7e4c2..692ed24899 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -127,6 +127,9 @@ struct OutputCompositionState { // 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 a56fc790b4..c88fbd6d0f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -118,9 +118,9 @@ public: 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 f2acfc9e6e..469fb386fc 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -255,10 +255,10 @@ bool Display::chooseCompositionStrategy( 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 @@ void Display::applyClientTargetRequests(const ClientTargetProperty& clientTarget 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 fa3733b540..2ae80de42a 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -448,7 +448,7 @@ void Output::present(const compositionengine::CompositionRefreshArgs& refreshArg devOptRepaintFlash(refreshArgs); finishFrame(std::move(result)); - postFramebuffer(); + presentFrameAndReleaseLayers(); renderCachedSets(refreshArgs); } @@ -836,6 +836,7 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr editState().earliestPresentTime = refreshArgs.earliestPresentTime; editState().expectedPresentTime = refreshArgs.expectedPresentTime; + editState().frameInterval = refreshArgs.frameInterval; editState().powerCallback = refreshArgs.powerCallback; compositionengine::OutputLayer* peekThroughLayer = nullptr; @@ -1152,7 +1153,7 @@ void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& } } - postFramebuffer(); + presentFrameAndReleaseLayers(); std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay); @@ -1509,7 +1510,7 @@ bool Output::isPowerHintSessionEnabled() { return false; } -void Output::postFramebuffer() { +void Output::presentFrameAndReleaseLayers() { ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str()); ALOGV(__FUNCTION__); @@ -1520,7 +1521,7 @@ void Output::postFramebuffer() { auto& outputState = editState(); outputState.dirtyRegion.clear(); - auto frame = presentAndGetFrameFences(); + auto frame = presentFrame(); mRenderSurface->onPresentDisplayCompleted(); @@ -1590,7 +1591,7 @@ bool Output::getSkipColorTransform() const { 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 027004acf6..4a778d49ac 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -582,7 +582,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfGpuDisplay) { 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 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) { .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 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) { .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 @@ TEST_F(DisplayApplyLayerRequestsToLayersTest, applyClientTargetRequests) { } /* - * Display::presentAndGetFrameFences() + * Display::presentFrame() */ using DisplayPresentAndGetFrameFencesTest = DisplayWithLayersTestCommon; @@ -876,7 +876,7 @@ TEST_F(DisplayPresentAndGetFrameFencesTest, returnsNoFencesOnGpuDisplay) { 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 @@ TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) { .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 @@ struct DisplayFunctionalTest : public testing::Test { } }; -TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) { +TEST_F(DisplayFunctionalTest, presentFrameAndReleaseLayersCriticalCallsAreOrdered) { InSequence seq; mDisplay->editState().isEnabled = true; @@ -1068,7 +1068,7 @@ TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) { 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 6807c8e5d7..b2491d894c 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -54,9 +54,9 @@ public: 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)); @@ -148,7 +148,7 @@ public: getOverlaySupport, (), (const, override)); MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); MOCK_METHOD(status_t, notifyExpectedPresentIfRequired, - (PhysicalDisplayId, nsecs_t, int32_t, int32_t)); + (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index cdcb68d9fc..5537fcdcb5 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -2007,7 +2007,7 @@ struct OutputPresentTest : public testing::Test { 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 @@ TEST_F(OutputPresentTest, justInvokesChildFunctionsInSequence) { 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 @@ TEST_F(OutputPresentTest, predictingCompositionStrategyInvokesPrepareFrameAsync) 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 @@ struct OutputDevOptRepaintFlashTest : public testing::Test { 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 @@ TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) { 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 @@ TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfEnabled) { 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 @@ TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty 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 @@ struct OutputFinishFrameTest : public testing::Test { 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 @@ TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) { } /* - * 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 @@ struct OutputPostFramebufferTest : public testing::Test { TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) { mOutput.mState.isEnabled = false; - mOutput.postFramebuffer(); + mOutput.presentFrameAndReleaseLayers(); } TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) { @@ -3119,10 +3119,10 @@ TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCom // 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 @@ TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) { 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 @@ TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) { EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get()); }); - mOutput.postFramebuffer(); + mOutput.presentFrameAndReleaseLayers(); } TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) { @@ -3177,7 +3177,7 @@ TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) 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 @@ TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) 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 @@ TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) { 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 @@ TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) { 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/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 00590e66cb..d9318af371 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -223,9 +223,6 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) { } TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) { - mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2; - mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold; - auto& layerState1 = mTestLayers[0]->layerState; auto& layerState2 = mTestLayers[1]->layerState; @@ -235,6 +232,10 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) { }; initializeFlattener(layers); + + mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2; + mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold; + expectAllLayersFlattened(layers); } diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 5b6591aeac..2ffe92b028 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -63,13 +63,14 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mDisplayToken(args.displayToken), mSequenceId(args.sequenceId), mCompositionDisplay{args.compositionDisplay}, - mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())), - mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())), - mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())), + mActiveModeFPSTrace(concatId("ActiveModeFPS")), + mActiveModeFPSHwcTrace(concatId("ActiveModeFPS_HWC")), + mRenderFrameRateFPSTrace(concatId("RenderRateFPS")), mPhysicalOrientation(args.physicalOrientation), mIsPrimary(args.isPrimary), mRequestedRefreshRate(args.requestedRefreshRate), - mRefreshRateSelector(std::move(args.refreshRateSelector)) { + mRefreshRateSelector(std::move(args.refreshRateSelector)), + mDesiredActiveModeChanged(concatId("DesiredActiveModeChanged"), false) { mCompositionDisplay->editState().isSecure = args.isSecure; mCompositionDisplay->createRenderSurface( compositionengine::RenderSurfaceCreationArgsBuilder() diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index a044534964..a40f310711 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -269,6 +269,11 @@ public: void dump(utils::Dumper&) const; private: + template <size_t N> + inline std::string concatId(const char (&str)[N]) const { + return std::string(ftl::Concat(str, ' ', getId().value).str()); + } + const sp<SurfaceFlinger> mFlinger; HWComposer& mHwComposer; const wp<IBinder> mDisplayToken; @@ -316,8 +321,7 @@ private: mutable std::mutex mActiveModeLock; ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock); - TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) = - {ftl::Concat("DesiredActiveModeChanged-", getId().value).c_str(), false}; + TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock); ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext); bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 1643ad0659..2d957e6334 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -24,6 +24,7 @@ #include <android-base/file.h> #include <android/binder_ibinder_platform.h> #include <android/binder_manager.h> +#include <common/FlagManager.h> #include <gui/TraceUtils.h> #include <log/log.h> #include <utils/Trace.h> @@ -281,7 +282,7 @@ bool AidlComposer::isSupported(OptionalFeature feature) const { } bool AidlComposer::getDisplayConfigurationsSupported() const { - return mComposerInterfaceVersion >= 3; + return mComposerInterfaceVersion >= 3 && FlagManager::getInstance().vrr_config(); } std::vector<Capability> AidlComposer::getCapabilities() { @@ -749,7 +750,8 @@ Error AidlComposer::setClientTargetSlotCount(Display display) { } 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 +760,8 @@ Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime 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 +779,9 @@ Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime } 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 +791,8 @@ Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPr 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 7693a80a76..1635a16ec8 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -134,12 +134,13 @@ public: 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 6704d88696..082717a65b 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -174,11 +174,13 @@ public: 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/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h index f32fb3a5c7..ba0825c5af 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayMode.h +++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h @@ -29,8 +29,8 @@ #include <scheduler/Fps.h> +#include <common/FlagManager.h> #include "DisplayHardware/Hal.h" -#include "FlagManager.h" #include "Scheduler/StrongTyping.h" namespace android { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 0c2b77de7d..bc763b2e55 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -517,11 +517,12 @@ Error Display::setVsyncEnabled(Vsync enabled) 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 @@ Error Display::validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes, 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 23dd3e5016..e7f807f553 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -148,9 +148,10 @@ public: 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 @@ public: 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 fb6089dfbe..6be57d480d 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -78,6 +78,59 @@ using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; namespace hal = android::hardware::graphics::composer::hal; +namespace { +bool isFrameIntervalOnCadence(android::TimePoint expectedPresentTime, + android::TimePoint lastExpectedPresentTimestamp, + android::Fps lastFrameInterval, android::Period timeout, + android::Duration threshold) { + if (lastFrameInterval.getPeriodNsecs() == 0) { + return false; + } + + const auto expectedPresentTimeDeltaNs = + expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns(); + + if (expectedPresentTimeDeltaNs > timeout.ns()) { + return false; + } + + const auto expectedPresentPeriods = static_cast<nsecs_t>( + std::round(static_cast<float>(expectedPresentTimeDeltaNs) / + static_cast<float>(lastFrameInterval.getPeriodNsecs()))); + const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods; + const auto calculatedExpectedPresentTimeNs = + lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs; + const auto presentTimeDelta = + std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs); + return presentTimeDelta < threshold.ns(); +} + +bool isExpectedPresentWithinTimeout(android::TimePoint expectedPresentTime, + android::TimePoint lastExpectedPresentTimestamp, + std::optional<android::Period> timeoutOpt, + android::Duration threshold) { + if (!timeoutOpt) { + // Always within timeout if timeoutOpt is absent and don't send hint + // for the timeout + return true; + } + + if (timeoutOpt->ns() == 0) { + // Always outside timeout if timeoutOpt is 0 and always send + // the hint for the timeout. + return false; + } + + if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) { + return true; + } + + // Check if within the threshold as it can be just outside the timeout + return std::abs(expectedPresentTime.ns() - + (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); +} +} // namespace + namespace android { HWComposer::~HWComposer() = default; @@ -445,7 +498,7 @@ status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot, 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(); @@ -485,12 +538,17 @@ status_t HWComposer::getDeviceCompositionChanges( }(); displayData.validateWasSkipped = false; - displayData.lastExpectedPresentTimestamp = expectedPresentTime; + { + std::scoped_lock lock{displayData.expectedPresentLock}; + displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime); + // TODO(b/296636176) Update displayData.lastFrameInterval for present display commands + } + 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); } @@ -505,7 +563,8 @@ status_t HWComposer::getDeviceCompositionChanges( } // 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)) { @@ -879,21 +938,44 @@ status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId } status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, - nsecs_t expectedPresentTime, - int32_t frameIntervalNs, int32_t timeoutNs) { + Period vsyncPeriod, + TimePoint expectedPresentTime, + Fps frameInterval, + std::optional<Period> timeoutOpt) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - auto& displayData = mDisplayData[displayId]; - if (expectedPresentTime >= displayData.lastExpectedPresentTimestamp && - expectedPresentTime < displayData.lastExpectedPresentTimestamp + timeoutNs) { - return NO_ERROR; - } + { + std::scoped_lock lock{displayData.expectedPresentLock}; + const auto lastExpectedPresentTimestamp = displayData.lastExpectedPresentTimestamp; + const auto lastFrameInterval = displayData.lastFrameInterval; + displayData.lastFrameInterval = frameInterval; + const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); + + const constexpr nsecs_t kOneSecondNs = + std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); + const bool frameIntervalIsOnCadence = + isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, + lastFrameInterval, + Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 + ? timeoutOpt->ns() + : kOneSecondNs), + threshold); + + const bool expectedPresentWithinTimeout = + isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, + timeoutOpt, threshold); + + if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + return NO_ERROR; + } - displayData.lastExpectedPresentTimestamp = expectedPresentTime; + displayData.lastExpectedPresentTimestamp = expectedPresentTime; + } ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__, - expectedPresentTime, frameIntervalNs); + expectedPresentTime, frameInterval.getPeriodNsecs()); const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(), - expectedPresentTime, frameIntervalNs); + expectedPresentTime.ns(), + frameInterval.getPeriodNsecs()); if (error != hal::Error::NONE) { ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str()); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 726a8eafbf..5846c07f87 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -147,7 +147,8 @@ public: 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; @@ -301,9 +302,10 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) = 0; virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; - virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime, - int32_t frameIntervalNs, - int32_t timeoutNs) = 0; + virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, + TimePoint expectedPresentTime, + Fps frameInterval, + std::optional<Period> timeoutOpt) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -346,7 +348,7 @@ public: 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, @@ -462,8 +464,9 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) override; status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; - status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime, - int32_t frameIntervalNs, int32_t timeoutNs) override; + status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, + TimePoint expectedPresentTime, Fps frameInterval, + std::optional<Period> timeoutOpt) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; @@ -497,7 +500,10 @@ private: sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires nsecs_t lastPresentTimestamp = 0; - nsecs_t lastExpectedPresentTimestamp = 0; + std::mutex expectedPresentLock; + TimePoint lastExpectedPresentTimestamp GUARDED_BY(expectedPresentLock) = + TimePoint::fromNs(0); + Fps lastFrameInterval GUARDED_BY(expectedPresentLock) = Fps::fromValue(0); std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index c13e568936..1e7c6da592 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -665,7 +665,8 @@ Error HidlComposer::setClientTargetSlotCount(Display display) { } 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::validateDisplay(Display display, nsecs_t /*expectedPresentTi } 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 1004ddd011..5c19b47b00 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -236,12 +236,13 @@ public: 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/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index f00ef671ad..e005ad3e03 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -251,7 +251,12 @@ void PowerAdvisor::reportActualWorkDuration() { actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin); mActualDuration = actualDuration; WorkDuration duration; + duration.workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(); + // TODO(b/284324521): Correctly calculate total duration. duration.durationNanos = actualDuration->ns(); + duration.cpuDurationNanos = actualDuration->ns(); + // TODO(b/284324521): Calculate RenderEngine GPU time. + duration.gpuDurationNanos = 0; duration.timeStampNanos = TimePoint::now().ns(); mHintSessionQueue.push_back(duration); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 1b1307b13a..dfbb55dacc 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -1074,6 +1074,66 @@ void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, }); } +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::traceActuals(pid_t surfaceFlingerPid, }); } -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 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBoo 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 @@ void FrameTimeline::flushPendingPresentFences() { 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 @@ void FrameTimeline::flushPendingPresentFences() { 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 538ea1288b..6598e218fd 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -378,7 +378,8 @@ public: // 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 @@ public: 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/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 7a85da056e..38974a2f58 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -332,6 +332,14 @@ FloatRect LayerSnapshot::sourceBounds() const { return geomBufferSize.toFloatRect(); } +bool LayerSnapshot::isFrontBuffered() const { + if (!externalTexture) { + return false; + } + + return externalTexture->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER; +} + Hwc2::IComposerClient::BlendMode LayerSnapshot::getBlendMode( const RequestedLayerState& requested) const { auto blendMode = Hwc2::IComposerClient::BlendMode::NONE; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index 4fd6495422..73ee22fa21 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -143,6 +143,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { std::string getIsVisibleReason() const; bool hasInputInfo() const; FloatRect sourceBounds() const; + bool isFrontBuffered() const; Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const; friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj); void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges, diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 2a0857d4dd..9476ff4932 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -739,6 +739,7 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a !snapshot.changes.test(RequestedLayerState::Changes::Created)) { if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Geometry | + RequestedLayerState::Changes::BufferSize | RequestedLayerState::Changes::Input)) { updateInput(snapshot, requested, parentSnapshot, path, args); } @@ -1095,6 +1096,8 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::TRUSTED_OVERLAY; } + snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize(); + // If the layer is a clone, we need to crop the input region to cloned root to prevent // touches from going outside the cloned area. if (path.isClone()) { diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 700baa22a6..0c0639ee83 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -136,6 +136,7 @@ using frontend::RoundedCornerState; using gui::GameMode; using gui::LayerMetadata; using gui::WindowInfo; +using ui::Size; using PresentState = frametimeline::SurfaceFrame::PresentState; @@ -165,6 +166,7 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mDrawingState.sequence = 0; mDrawingState.transform.set(0, 0); mDrawingState.frameNumber = 0; + mDrawingState.previousFrameNumber = 0; mDrawingState.barrierFrameNumber = 0; mDrawingState.producerId = 0; mDrawingState.barrierProducerId = 0; @@ -2591,6 +2593,9 @@ WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) { } } + Rect bufferSize = getBufferSize(getDrawingState()); + info.contentSize = Size(bufferSize.width(), bufferSize.height()); + return info; } @@ -2931,7 +2936,6 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, break; } } - if (ch != nullptr) { ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); @@ -2940,6 +2944,10 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, if (mBufferInfo.mBuffer) { mPreviouslyPresentedLayerStacks.push_back(layerStack); } + + if (mDrawingState.frameNumber > 0) { + mDrawingState.previousFrameNumber = mDrawingState.frameNumber; + } } void Layer::onSurfaceFrameCreated( @@ -3144,6 +3152,7 @@ void Layer::releasePreviousBuffer() { void Layer::resetDrawingStateBufferInfo() { mDrawingState.producerId = 0; mDrawingState.frameNumber = 0; + mDrawingState.previousFrameNumber = 0; mDrawingState.releaseBufferListener = nullptr; mDrawingState.buffer = nullptr; mDrawingState.acquireFence = sp<Fence>::make(-1); @@ -3420,6 +3429,7 @@ bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle // If this transaction set an acquire fence on this layer, set its acquire time handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence; handle->frameNumber = mDrawingState.frameNumber; + handle->previousFrameNumber = mDrawingState.previousFrameNumber; // Store so latched time and release fence can be set mDrawingState.callbackHandles.push_back(handle); @@ -4024,10 +4034,10 @@ bool Layer::isVisible() const { return getAlpha() > 0.0f || hasBlur(); } -void Layer::onPostComposition(const DisplayDevice* display, - const std::shared_ptr<FenceTime>& glDoneFence, - const std::shared_ptr<FenceTime>& presentFence, - const CompositorTiming& compositorTiming) { +void Layer::onCompositionPresented(const DisplayDevice* display, + const std::shared_ptr<FenceTime>& glDoneFence, + const std::shared_ptr<FenceTime>& presentFence, + const CompositorTiming& compositorTiming) { // mFrameLatencyNeeded is true when a new frame was latched for the // composition. if (!mBufferInfo.mFrameLatencyNeeded) return; @@ -4230,6 +4240,14 @@ ui::Dataspace Layer::getDataSpace() const { return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace; } +bool Layer::isFrontBuffered() const { + if (mBufferInfo.mBuffer == nullptr) { + return false; + } + + return mBufferInfo.mBuffer->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER; +} + ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) { ui::Dataspace updatedDataspace = dataspace; // translate legacy dataspaces to modern dataspaces diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index dd91adcba7..28168c3f65 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -138,6 +138,7 @@ public: ui::Dataspace dataspace; uint64_t frameNumber; + uint64_t previousFrameNumber; // high watermark framenumber to use to check for barriers to protect ourselves // from out of order transactions uint64_t barrierFrameNumber; @@ -342,6 +343,8 @@ public: // ui::Dataspace getDataSpace() const; + virtual bool isFrontBuffered() const; + virtual sp<LayerFE> getCompositionEngineLayerFE() const; virtual sp<LayerFE> copyCompositionEngineLayerFE() const; sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); @@ -433,13 +436,10 @@ public: void updateCloneBufferInfo(); uint64_t mPreviousFrameNumber = 0; - /* - * called after composition. - * returns true if the layer latched a new buffer this frame. - */ - void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/, - const std::shared_ptr<FenceTime>& /*presentFence*/, - const CompositorTiming&); + void onCompositionPresented(const DisplayDevice*, + const std::shared_ptr<FenceTime>& /*glDoneFence*/, + const std::shared_ptr<FenceTime>& /*presentFence*/, + const CompositorTiming&); // If a buffer was replaced this frame, release the former buffer void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/); @@ -915,14 +915,13 @@ public: void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now); void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now); auto getLayerProps() const { - return scheduler::LayerProps{ - .visible = isVisible(), - .bounds = getBounds(), - .transform = getTransform(), - .setFrameRateVote = getFrameRateForLayerTree(), - .frameRateSelectionPriority = getFrameRateSelectionPriority(), - .isSmallDirty = mSmallDirty, - }; + return scheduler::LayerProps{.visible = isVisible(), + .bounds = getBounds(), + .transform = getTransform(), + .setFrameRateVote = getFrameRateForLayerTree(), + .frameRateSelectionPriority = getFrameRateSelectionPriority(), + .isSmallDirty = mSmallDirty, + .isFrontBuffered = isFrontBuffered()}; }; bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; } void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) { diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 2ac7319709..b960e33682 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -16,8 +16,8 @@ #include <algorithm> +#include <common/FlagManager.h> #include "Client.h" -#include "FlagManager.h" #include "Layer.h" #include "RefreshRateOverlay.h" @@ -268,8 +268,7 @@ void RefreshRateOverlay::changeRefreshRate(Fps vsyncRate, Fps renderFps) { } 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/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 7f627f829d..693a357de5 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -43,9 +43,9 @@ #include <utils/Errors.h> #include <utils/Trace.h> +#include <common/FlagManager.h> #include <scheduler/VsyncConfig.h> #include "DisplayHardware/DisplayMode.h" -#include "FlagManager.h" #include "FrameTimeline.h" #include "VSyncDispatch.h" #include "VSyncTracker.h" diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 21bbb08c1c..d309adccf8 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -31,9 +31,9 @@ #include <string> #include <utility> +#include <common/FlagManager.h> #include "../Layer.h" #include "EventThread.h" -#include "FlagManager.h" #include "LayerInfo.h" namespace android::scheduler { @@ -51,6 +51,11 @@ bool isLayerActive(const LayerInfo& info, nsecs_t threshold) { return true; } + // Make all front buffered layers active + if (FlagManager::getInstance().vrr_config() && info.isFrontBuffered() && info.isVisible()) { + return true; + } + return info.isVisible() && info.getLastUpdatedTime() >= threshold; } @@ -162,6 +167,27 @@ void LayerHistory::setDefaultFrameRateCompatibility(int32_t id, 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 bac1ec639a..5a9445bcaf 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -73,7 +73,7 @@ public: // 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 54e9022add..bf3a7bc8b7 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -75,6 +75,10 @@ void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUp } } +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()) @@ -334,6 +338,14 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec return votes; } + // Vote for max refresh rate whenever we're front-buffered. + if (FlagManager::getInstance().vrr_config() && isFrontBuffered()) { + ATRACE_FORMAT_INSTANT("front buffered"); + ALOGV("%s is front-buffered", mName.c_str()); + votes.push_back({LayerHistory::LayerVoteType::Max, Fps()}); + return votes; + } + const LayerInfo::Frequent frequent = isFrequent(now); mIsFrequencyConclusive = frequent.isConclusive; if (!frequent.isFrequent) { @@ -394,6 +406,10 @@ int32_t LayerInfo::getFrameRateSelectionPriority() const { return mLayerProps->frameRateSelectionPriority; } +bool LayerInfo::isFrontBuffered() const { + return mLayerProps->isFrontBuffered; +} + FloatRect LayerInfo::getBounds() const { return mLayerProps->bounds; } diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 7d3cffabf7..d24fc33491 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -182,6 +182,8 @@ public: // 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}; @@ -200,6 +202,7 @@ public: FrameRate getSetFrameRateVote() const; bool isVisible() const; int32_t getFrameRateSelectionPriority() const; + bool isFrontBuffered() const; FloatRect getBounds() const; ui::Transform getTransform() const; @@ -360,6 +363,7 @@ struct LayerProps { LayerInfo::FrameRate setFrameRateVote; int32_t frameRateSelectionPriority = -1; bool isSmallDirty = false; + bool isFrontBuffered = false; }; } // namespace scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index 5892b2b44c..47c8ef9f16 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -1487,7 +1487,7 @@ FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory catego case FrameRateCategory::Normal: return FpsRange{60_Hz, 90_Hz}; case FrameRateCategory::Low: - return FpsRange{30_Hz, 60_Hz}; + return FpsRange{30_Hz, 30_Hz}; case FrameRateCategory::NoPreference: case FrameRateCategory::Default: LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s", diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 1a8713d4a9..b54f33451b 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -45,9 +45,9 @@ #include <memory> #include <numeric> +#include <common/FlagManager.h> #include "../Layer.h" #include "EventThread.h" -#include "FlagManager.h" #include "FrameRateOverrideMappings.h" #include "FrontEnd/LayerHandle.h" #include "OneShotTimer.h" @@ -67,11 +67,12 @@ namespace android::scheduler { Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features, - sp<VsyncModulator> modulatorPtr) + sp<VsyncModulator> modulatorPtr, IVsyncTrackerCallback& vsyncTrackerCallback) : impl::MessageQueue(compositor), mFeatures(features), mVsyncModulator(std::move(modulatorPtr)), - mSchedulerCallback(callback) {} + mSchedulerCallback(callback), + mVsyncTrackerCallback(vsyncTrackerCallback) {} Scheduler::~Scheduler() { // MessageQueue depends on VsyncSchedule, so first destroy it. @@ -116,10 +117,10 @@ void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetter } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { - auto schedulePtr = std::make_shared<VsyncSchedule>(displayId, mFeatures, - [this](PhysicalDisplayId id, bool enable) { - onHardwareVsyncRequest(id, enable); - }); + auto schedulePtr = std::make_shared<VsyncSchedule>( + displayId, mFeatures, + [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); }, + mVsyncTrackerCallback); registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr)); } @@ -562,7 +563,19 @@ void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) { ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(), to_string(mode.modePtr->getVsyncRate()).c_str()); - display.schedulePtr->getTracker().setRenderRate(renderFrameRate); + display.schedulePtr->getTracker().setDisplayModeData( + {.renderRate = renderFrameRate, + .notifyExpectedPresentTimeoutOpt = getNotifyExpectedPresentTimeout(mode)}); +} + +std::optional<Period> Scheduler::getNotifyExpectedPresentTimeout(const FrameRateMode& mode) { + if (mode.modePtr->getVrrConfig() && mode.modePtr->getVrrConfig()->notifyExpectedPresentConfig) { + return Period::fromNs( + mode.modePtr->getVrrConfig() + ->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs); + } else { + return std::nullopt; + } } void Scheduler::resync() { @@ -641,6 +654,10 @@ void Scheduler::setDefaultFrameRateCompatibility( 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) { @@ -1165,7 +1182,7 @@ void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimel } } -bool Scheduler::onPostComposition(nsecs_t presentTime) { +bool Scheduler::onCompositionPresented(nsecs_t presentTime) { std::lock_guard<std::mutex> lock(mVsyncTimelineLock); if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) { if (presentTime < mLastVsyncPeriodChangeTimeline->refreshTimeNanos) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index b0520a61ff..c78051a278 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -102,7 +102,8 @@ class Scheduler : public IEventThreadCallback, android::impl::MessageQueue { using Impl = android::impl::MessageQueue; public: - Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>); + Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>, + IVsyncTrackerCallback&); virtual ~Scheduler(); void startTimers(); @@ -233,6 +234,7 @@ public: 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); @@ -281,8 +283,8 @@ public: // Notifies the scheduler about a refresh rate timeline change. void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline); - // Notifies the scheduler post composition. Returns if recomposite is needed. - bool onPostComposition(nsecs_t presentTime); + // Notifies the scheduler once the composition is presented. Returns if recomposite is needed. + bool onCompositionPresented(nsecs_t presentTime); // Notifies the scheduler when the display size has changed. Called from SF's main thread void onActiveDisplayAreaChanged(uint32_t displayArea); @@ -429,6 +431,9 @@ private: Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); void resync() override EXCLUDES(mDisplayLock); + std::optional<Period> getNotifyExpectedPresentTimeout(const FrameRateMode&) + REQUIRES(mDisplayLock); + // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { sp<EventThreadConnection> connection; @@ -462,6 +467,8 @@ private: ISchedulerCallback& mSchedulerCallback; + IVsyncTrackerCallback& mVsyncTrackerCallback; + // mDisplayLock may be locked while under mPolicyLock. mutable std::mutex mPolicyLock; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 186a6bc9d6..ef30887037 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -25,7 +25,7 @@ #include <scheduler/TimeKeeper.h> -#include "FlagManager.h" +#include <common/FlagManager.h> #include "VSyncDispatchTimerQueue.h" #include "VSyncTracker.h" @@ -38,19 +38,16 @@ using base::StringAppendF; namespace { -nsecs_t getExpectedCallbackTime(nsecs_t now, nsecs_t nextVsyncTime, +nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime, const VSyncDispatch::ScheduleTiming& timing) { - const auto expectedCallbackTime = nextVsyncTime - timing.readyDuration - timing.workDuration; - const auto baseTime = - FlagManager::getInstance().dont_skip_on_early() ? now : expectedCallbackTime; - return std::max(baseTime, expectedCallbackTime); + return nextVsyncTime - timing.readyDuration - timing.workDuration; } nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now, const VSyncDispatch::ScheduleTiming& timing) { const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom( std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration)); - return getExpectedCallbackTime(now, nextVsyncTime, timing); + return getExpectedCallbackTime(nextVsyncTime, timing); } } // namespace @@ -106,21 +103,23 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance))); if (FlagManager::getInstance().dont_skip_on_early()) { if (wouldSkipAVsyncTarget || wouldSkipAWakeup) { - return getExpectedCallbackTime(now, mArmedInfo->mActualVsyncTime, timing); + nextVsyncTime = mArmedInfo->mActualVsyncTime; + } else { + nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime); } + nextWakeupTime = std::max(now, nextVsyncTime - timing.workDuration - timing.readyDuration); } else { if (wouldSkipAVsyncTarget && wouldSkipAWakeup) { - return getExpectedCallbackTime(now, nextVsyncTime, timing); + return getExpectedCallbackTime(nextVsyncTime, timing); } + nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime); + nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; } - nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime); - nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; - auto const nextReadyTime = nextVsyncTime - timing.readyDuration; mScheduleTiming = timing; mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime}; - return getExpectedCallbackTime(now, nextVsyncTime, timing); + return nextWakeupTime; } void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) { diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index e969fdc679..f5f93ce2f1 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -29,6 +29,7 @@ #include <android-base/logging.h> #include <android-base/stringprintf.h> +#include <common/FlagManager.h> #include <cutils/compiler.h> #include <cutils/properties.h> #include <ftl/concat.h> @@ -47,12 +48,14 @@ static auto constexpr kMaxPercent = 100u; VSyncPredictor::~VSyncPredictor() = default; VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent) + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, + IVsyncTrackerCallback& callback) : mId(id), mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), kHistorySize(historySize), kMinimumSamplesForPrediction(minimumSamplesForPrediction), kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)), + mVsyncTrackerCallback(callback), mIdealPeriod(idealPeriod) { resetModel(); } @@ -275,11 +278,11 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { mLastVsyncSequence = getVsyncSequenceLocked(timePoint); const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int { - if (!mRenderRate) return 0; + if (!mDisplayModeDataOpt) return 0; const auto divisor = RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), - *mRenderRate); + mDisplayModeDataOpt->renderRate); if (divisor <= 1) return 0; const int mod = mLastVsyncSequence->seq % divisor; @@ -289,12 +292,29 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { }(); if (renderRatePhase == 0) { - return mLastVsyncSequence->vsyncTime; + const auto vsyncTime = mLastVsyncSequence->vsyncTime; + if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) { + const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime); + ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__, + ticks<std::milli, float>(vsyncTimePoint - TimePoint::now())); + mVsyncTrackerCallback.onVsyncGenerated(mId, vsyncTimePoint, *mDisplayModeDataOpt, + Period::fromNs(mIdealPeriod)); + } + return vsyncTime; } auto const [slope, intercept] = getVSyncPredictionModelLocked(); const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase; - return nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); + const auto nextAnticipatedVsyncTime = + nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); + if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) { + const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime); + ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__, + ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now())); + mVsyncTrackerCallback.onVsyncGenerated(mId, nextAnticipatedVsyncTimePoint, + *mDisplayModeDataOpt, Period::fromNs(mIdealPeriod)); + } + return nextAnticipatedVsyncTime; } /* @@ -332,10 +352,14 @@ bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) c return vsyncSequence.seq % divisor == 0; } -void VSyncPredictor::setRenderRate(Fps fps) { - ALOGV("%s %s: %s", __func__, to_string(mId).c_str(), to_string(fps).c_str()); +void VSyncPredictor::setDisplayModeData(const DisplayModeData& displayModeData) { + ALOGV("%s %s: RenderRate %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(), + to_string(displayModeData.renderRate).c_str(), + displayModeData.notifyExpectedPresentTimeoutOpt + ? std::to_string(displayModeData.notifyExpectedPresentTimeoutOpt->ns()).c_str() + : "N/A"); std::lock_guard lock(mMutex); - mRenderRate = fps; + mDisplayModeDataOpt = displayModeData; } VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { @@ -358,6 +382,7 @@ void VSyncPredictor::setPeriod(nsecs_t period) { mRateMap.erase(mRateMap.begin()); } + // TODO(b/308610306) mIdealPeriod to be updated with setDisplayModeData mIdealPeriod = period; if (mRateMap.find(period) == mRateMap.end()) { mRateMap[mIdealPeriod] = {period, 0}; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index c01c44dc6b..c271eb738e 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -36,9 +36,11 @@ public: * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. + * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker. */ VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent); + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, + IVsyncTrackerCallback&); ~VSyncPredictor(); bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); @@ -69,7 +71,7 @@ public: bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); - void setRenderRate(Fps) final EXCLUDES(mMutex); + void setDisplayModeData(const DisplayModeData&) final EXCLUDES(mMutex); void dump(std::string& result) const final EXCLUDES(mMutex); @@ -99,6 +101,7 @@ private: size_t const kHistorySize; size_t const kMinimumSamplesForPrediction; size_t const kOutlierTolerancePercent; + IVsyncTrackerCallback& mVsyncTrackerCallback; std::mutex mutable mMutex; nsecs_t mIdealPeriod GUARDED_BY(mMutex); @@ -110,7 +113,7 @@ private: size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); - std::optional<Fps> mRenderRate GUARDED_BY(mMutex); + std::optional<DisplayModeData> mDisplayModeDataOpt GUARDED_BY(mMutex); mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex); }; diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index bc0e3bcbb2..7eedc312e2 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -16,6 +16,7 @@ #pragma once +#include <ui/DisplayId.h> #include <utils/Timers.h> #include <scheduler/Fps.h> @@ -23,6 +24,23 @@ #include "VSyncDispatch.h" namespace android::scheduler { + +struct DisplayModeData { + Fps renderRate; + std::optional<Period> notifyExpectedPresentTimeoutOpt; + + bool operator==(const DisplayModeData& other) const { + return isApproxEqual(renderRate, other.renderRate) && + notifyExpectedPresentTimeoutOpt == other.notifyExpectedPresentTimeoutOpt; + } +}; + +struct IVsyncTrackerCallback { + virtual ~IVsyncTrackerCallback() = default; + virtual void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime, + const DisplayModeData&, Period vsyncPeriod) = 0; +}; + /* * VSyncTracker is an interface for providing estimates on future Vsync signal times based on * historical vsync timing data. @@ -80,16 +98,20 @@ public: virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; /* - * Sets a render rate on the tracker. If the render rate is not a divisor - * of the period, the render rate is ignored until the period changes. + * Sets the metadata about the currently active display mode such as VRR + * timeout period, vsyncPeriod and framework property such as render rate. + * If the render rate is not a divisor of the period, the render rate is + * ignored until the period changes. * The tracker will continue to track the vsync timeline and expect it * to match the current period, however, nextAnticipatedVSyncTimeFrom will * return vsyncs according to the render rate set. Setting a render rate is useful * when a display is running at 120Hz but the render frame rate is 60Hz. + * When IVsyncTrackerCallback::onVsyncGenerated callback is made we will pass along + * the vsyncPeriod, render rate and timeoutNs. * - * \param [in] Fps The render rate the tracker should operate at. + * \param [in] DisplayModeData The DisplayModeData the tracker will use. */ - virtual void setRenderRate(Fps) = 0; + virtual void setDisplayModeData(const DisplayModeData&) = 0; virtual void dump(std::string& result) const = 0; diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index ff3f29dbbf..5fb53f9e20 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -54,10 +54,11 @@ private: }; VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features, - RequestHardwareVsync requestHardwareVsync) + RequestHardwareVsync requestHardwareVsync, + IVsyncTrackerCallback& callback) : mId(id), mRequestHardwareVsync(std::move(requestHardwareVsync)), - mTracker(createTracker(id)), + mTracker(createTracker(id, callback)), mDispatch(createDispatch(mTracker)), mController(createController(id, *mTracker, features)), mTracer(features.test(Feature::kTracePredictedVsync) @@ -100,7 +101,8 @@ void VsyncSchedule::dump(std::string& out) const { mDispatch->dump(out); } -VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) { +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id, + IVsyncTrackerCallback& callback) { // TODO(b/144707443): Tune constants. constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs(); constexpr size_t kHistorySize = 20; @@ -108,7 +110,8 @@ VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) { constexpr uint32_t kDiscardOutlierPercent = 20; return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize, - kMinSamplesForPrediction, kDiscardOutlierPercent); + kMinSamplesForPrediction, kDiscardOutlierPercent, + callback); } VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) { diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 47e92e107d..ca61f875c3 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -31,6 +31,7 @@ #include <scheduler/Time.h> #include "ThreadContext.h" +#include "VSyncTracker.h" namespace android { class EventThreadTest; @@ -56,7 +57,7 @@ class VsyncSchedule final : public IVsyncSource { public: using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>; - VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync); + VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync, IVsyncTrackerCallback&); ~VsyncSchedule(); // IVsyncSource overrides: @@ -124,7 +125,7 @@ private: friend class android::VsyncScheduleTest; friend class android::fuzz::SchedulerFuzzer; - static TrackerPtr createTracker(PhysicalDisplayId); + static TrackerPtr createTracker(PhysicalDisplayId, IVsyncTrackerCallback&); static DispatchPtr createDispatch(TrackerPtr); static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags); diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h index 19e951abb2..2806450c5f 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h @@ -83,7 +83,7 @@ struct FpsRanges { }; // 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 46514c5a34..5cc29855a2 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -113,6 +113,7 @@ #include <unordered_map> #include <vector> +#include <common/FlagManager.h> #include <gui/LayerStatePermissions.h> #include <gui/SchedulingPolicy.h> #include <ui/DisplayIdentification.h> @@ -129,7 +130,6 @@ #include "DisplayHardware/VirtualDisplaySurface.h" #include "DisplayRenderArea.h" #include "Effects/Daltonizer.h" -#include "FlagManager.h" #include "FpsReporter.h" #include "FrameTimeline/FrameTimeline.h" #include "FrameTracer/FrameTracer.h" @@ -875,7 +875,7 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache(shouldPrimeUltraHDR); if (setSchedFifo(true) != NO_ERROR) { - ALOGW("Can't set SCHED_OTHER for primeCache"); + ALOGW("Can't set SCHED_FIFO after primeCache"); } } @@ -2219,7 +2219,8 @@ void SurfaceFlinger::updateLayerHistory(nsecs_t now) { 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; @@ -2247,8 +2248,13 @@ void SurfaceFlinger::updateLayerHistory(nsecs_t now) { .setFrameRateVote = snapshot->frameRate, .frameRateSelectionPriority = snapshot->frameRateSelectionPriority, .isSmallDirty = snapshot->isSmallDirty, + .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); @@ -2620,6 +2626,10 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( 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); @@ -2747,11 +2757,11 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( mPowerAdvisor->reportActualWorkDuration(); } - if (mScheduler->onPostComposition(presentTime)) { + if (mScheduler->onCompositionPresented(presentTime)) { scheduleComposite(FrameHint::kNone); } - postComposition(pacesetterId, frameTargeters, presentTime); + onCompositionPresented(pacesetterId, frameTargeters, presentTime); const bool hadGpuComposited = multiDisplayUnion(mCompositionCoverage).test(CompositionCoverage::Gpu); @@ -2908,11 +2918,10 @@ ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId, return ui::ROTATION_0; } -void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId, - const scheduler::FrameTargeters& frameTargeters, - nsecs_t presentStartTime) { +void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, + const scheduler::FrameTargeters& frameTargeters, + nsecs_t presentStartTime) { ATRACE_CALL(); - ALOGV(__func__); ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences; ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences; @@ -3004,8 +3013,9 @@ void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId, mLayersWithBuffersRemoved.clear(); for (const auto& layer: mLayersWithQueuedFrames) { - layer->onPostComposition(pacesetterDisplay.get(), pacesetterGpuCompositionDoneFenceTime, - pacesetterPresentFenceTime, compositorTiming); + layer->onCompositionPresented(pacesetterDisplay.get(), + pacesetterGpuCompositionDoneFenceTime, + pacesetterPresentFenceTime, compositorTiming); layer->releasePendingBuffer(presentTime.ns()); } @@ -3799,7 +3809,6 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { // first frame before the display is available, we rely // on WMS and DMS to provide the right information // so the client can calculate the hint. - ALOGV("Skipping reporting transform hint update for %s", layer->getDebugName()); layer->skipReportingTransformHint(); } else { layer->updateTransformHint(hintDisplay->getTransformHint()); @@ -4036,6 +4045,21 @@ void SurfaceFlinger::onChoreographerAttached() { } } +void SurfaceFlinger::onVsyncGenerated(PhysicalDisplayId displayId, TimePoint expectedPresentTime, + const scheduler::DisplayModeData& displayModeData, + Period vsyncPeriod) { + const auto status = + getHwComposer() + .notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime, + displayModeData.renderRate, + displayModeData + .notifyExpectedPresentTimeoutOpt); + if (status != NO_ERROR) { + ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__, + displayId.value); + } +} + void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { using namespace scheduler; @@ -4074,8 +4098,12 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features, - std::move(modulatorPtr)); + std::move(modulatorPtr), + static_cast<IVsyncTrackerCallback&>(*this)); mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); + if (FlagManager::getInstance().vrr_config()) { + mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps); + } mScheduler->startTimers(); const auto configs = mVsyncConfiguration->getCurrentConfigs(); @@ -4880,6 +4908,7 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin .transform = layer->getTransform(), .setFrameRateVote = layer->getFrameRateForLayerTree(), .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(), + .isFrontBuffered = layer->isFrontBuffered(), }; layer->recordLayerHistoryAnimationTx(layerProps, now); } @@ -5965,7 +5994,9 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { }); out << "\nLayer Hierarchy\n" - << mLayerHierarchyBuilder.getHierarchy() << "\n\n"; + << mLayerHierarchyBuilder.getHierarchy() + << "\nOffscreen Hierarchy\n" + << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n"; compositionLayers = out.str(); dumpHwcLayersMinidump(compositionLayers); } @@ -6245,7 +6276,9 @@ void SurfaceFlinger::dumpFrontEnd(std::string& result) { }); 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(); @@ -8386,7 +8419,8 @@ status_t SurfaceFlinger::setSmallAreaDetectionThreshold(int32_t appId, float thr 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/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 66cb8266dd..6e1b4f497e 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -63,13 +63,13 @@ #include <scheduler/interface/ICompositor.h> #include <ui/FenceResult.h> +#include <common/FlagManager.h> #include "Display/PhysicalDisplay.h" #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/PowerAdvisor.h" #include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" -#include "FlagManager.h" #include "FrontEnd/DisplayInfo.h" #include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/LayerLifecycleManager.h" @@ -195,7 +195,8 @@ class SurfaceFlinger : public BnSurfaceComposer, private HWC2::ComposerCallback, private ICompositor, private scheduler::ISchedulerCallback, - private compositionengine::ICEPowerCallback { + private compositionengine::ICEPowerCallback, + private scheduler::IVsyncTrackerCallback { public: struct SkipInitializationTag {}; @@ -656,6 +657,10 @@ private: // ICEPowerCallback overrides: void notifyCpuLoadUp() override; + // IVsyncTrackerCallback overrides + void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime, + const scheduler::DisplayModeData&, Period vsyncPeriod) override; + // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); @@ -982,8 +987,8 @@ private: /* * Compositing */ - void postComposition(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&, - nsecs_t presentStartTime) REQUIRES(kMainThreadContext); + void onCompositionPresented(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&, + nsecs_t presentStartTime) REQUIRES(kMainThreadContext); /* * Display management diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index 3587a726cd..6a155c17df 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -158,7 +158,7 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle->previousReleaseFence = prevFence; handle->previousReleaseFences.clear(); - FrameEventHistoryStats eventStats(handle->frameNumber, + FrameEventHistoryStats eventStats(handle->frameNumber, handle->previousFrameNumber, handle->gpuCompositionDoneFence->getSnapshot().fence, handle->compositorTiming, handle->refreshStartTime, handle->dequeueReadyTime); diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index 3074795f62..245398f2f4 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -56,6 +56,7 @@ public: nsecs_t refreshStartTime = 0; nsecs_t dequeueReadyTime = 0; uint64_t frameNumber = 0; + uint64_t previousFrameNumber = 0; ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; }; diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp new file mode 100644 index 0000000000..5ef22b590b --- /dev/null +++ b/services/surfaceflinger/common/Android.bp @@ -0,0 +1,48 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_defaults { + name: "libsurfaceflinger_common_defaults", + defaults: [ + "android.hardware.graphics.composer3-ndk_shared", + "surfaceflinger_defaults", + ], + shared_libs: [ + "libSurfaceFlingerProp", + "server_configurable_flags", + ], + static_libs: [ + "librenderengine", + ], + srcs: [ + "FlagManager.cpp", + ], + local_include_dirs: ["include"], + export_include_dirs: ["include"], +} + +cc_library_static { + name: "libsurfaceflinger_common", + defaults: [ + "libsurfaceflinger_common_defaults", + ], + static_libs: [ + "libsurfaceflingerflags", + ], +} + +cc_library_static { + name: "libsurfaceflinger_common_test", + defaults: [ + "libsurfaceflinger_common_defaults", + ], + static_libs: [ + "libsurfaceflingerflags_test", + ], +} diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 5bbdfe91e6..8da7d8e92f 100644 --- a/services/surfaceflinger/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "FlagManager.h" +#include <common/FlagManager.h> #include <SurfaceFlingerProperties.sysprop.h> #include <android-base/parsebool.h> @@ -60,10 +60,6 @@ bool getFlagValue(std::function<bool()> getter, std::optional<bool> overrideValu return getter(); } -void dumpFlag(std::string& result, const char* name, std::function<bool()> getter) { - base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false"); -} - } // namespace const FlagManager& FlagManager::getInstance() { @@ -90,21 +86,44 @@ void FlagManager::setUnitTestMode() { mBootCompleted = true; } +void FlagManager::dumpFlag(std::string& result, bool readonly, const char* name, + std::function<bool()> getter) const { + if (readonly || mBootCompleted) { + base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false"); + } else { + base::StringAppendF(&result, "%s: in progress (still booting)\n", name); + } +} + void FlagManager::dump(std::string& result) const { -#define DUMP_FLAG(name) dumpFlag(result, #name, std::bind(&FlagManager::name, this)) +#define DUMP_FLAG_INTERVAL(name, readonly) \ + dumpFlag(result, (readonly), #name, std::bind(&FlagManager::name, this)) +#define DUMP_SERVER_FLAG(name) DUMP_FLAG_INTERVAL(name, false) +#define DUMP_READ_ONLY_FLAG(name) DUMP_FLAG_INTERVAL(name, true) base::StringAppendF(&result, "FlagManager values: \n"); - DUMP_FLAG(use_adpf_cpu_hint); - DUMP_FLAG(use_skia_tracing); - DUMP_FLAG(connected_display); - DUMP_FLAG(dont_skip_on_early); - DUMP_FLAG(enable_small_area_detection); - DUMP_FLAG(misc1); - DUMP_FLAG(late_boot_misc2); - DUMP_FLAG(vrr_config); - DUMP_FLAG(hotplug2); - -#undef DUMP_FLAG + + /// Legacy server flags /// + DUMP_SERVER_FLAG(use_adpf_cpu_hint); + DUMP_SERVER_FLAG(use_skia_tracing); + + /// 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); + DUMP_READ_ONLY_FLAG(enable_small_area_detection); + DUMP_READ_ONLY_FLAG(misc1); + DUMP_READ_ONLY_FLAG(vrr_config); + DUMP_READ_ONLY_FLAG(hotplug2); + DUMP_READ_ONLY_FLAG(hdcp_level_hal); + DUMP_READ_ONLY_FLAG(multithreaded_present); + +#undef DUMP_READ_ONLY_FLAG +#undef DUMP_SERVER_FLAG +#undef DUMP_FLAG_INTERVAL } std::optional<bool> FlagManager::getBoolProperty(const char* property) const { @@ -167,9 +186,12 @@ FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "") FLAG_MANAGER_READ_ONLY_FLAG(misc1, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config") FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "") +FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "") +FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present") /// 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/common/include/common/FlagManager.h index 9f83b1fc01..e3e4f80905 100644 --- a/services/surfaceflinger/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -46,16 +46,19 @@ public: bool use_adpf_cpu_hint() const; bool use_skia_tracing() const; + /// 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; bool enable_small_area_detection() const; bool misc1() const; bool vrr_config() const; bool hotplug2() const; - - /// Trunk stable server flags /// - bool late_boot_misc2() const; - bool dont_skip_on_early() const; + bool hdcp_level_hal() const; + bool multithreaded_present() const; protected: // overridden for unit tests @@ -67,6 +70,9 @@ private: FlagManager(const FlagManager&) = delete; + void dumpFlag(std::string& result, bool readonly, const char* name, + std::function<bool()> getter) const; + std::atomic_bool mBootCompleted = false; std::atomic_bool mUnitTestMode = false; diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp index 910e6852a9..ab3b3528dd 100644 --- a/services/surfaceflinger/fuzzer/Android.bp +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -39,6 +39,7 @@ cc_defaults { "libgtest_ndk_c++", "libgmock_main_ndk", "librenderengine_mocks", + "libsurfaceflinger_common", "perfetto_trace_protos", "libcompositionengine_mocks", "perfetto_trace_protos", @@ -74,9 +75,17 @@ cc_defaults { ], 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 f22315ab1e..afb5f5c630 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp @@ -163,16 +163,22 @@ private: 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 @@ void DisplayHardwareFuzzer::getDeviceCompositionChanges(HalDisplayId halDisplayI 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/fuzzer/surfaceflinger_frametracer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp index 8978971539..ce8d47e71c 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp @@ -30,12 +30,6 @@ constexpr int32_t kMinRange = 0; constexpr int32_t kConfigDuration = 500; constexpr int32_t kBufferSize = 1024; constexpr int32_t kTimeOffset = 100000; -constexpr perfetto::BackendType backendTypes[] = { - perfetto::kUnspecifiedBackend, - perfetto::kInProcessBackend, - perfetto::kSystemBackend, - perfetto::kCustomBackend, -}; class FrameTracerFuzzer { public: @@ -71,8 +65,7 @@ std::unique_ptr<perfetto::TracingSession> FrameTracerFuzzer::getTracingSessionFo auto* dsCfg = cfg.add_data_sources()->mutable_config(); dsCfg->set_name(android::FrameTracer::kFrameTracerDataSource); - auto tracingSession = - perfetto::Tracing::NewTrace(mFdp.PickValueInArray<perfetto::BackendType>(backendTypes)); + auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend); tracingSession->Setup(cfg); return tracingSession; } @@ -115,11 +108,15 @@ void FrameTracerFuzzer::process() { std::vector<int32_t> layerIds = generateLayerIds(mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds)); + std::unique_ptr<perfetto::TracingSession> tracingSession; while (mFdp.remaining_bytes()) { auto invokeFrametracerAPI = mFdp.PickValueInArray<const std::function<void()>>({ [&]() { mFrameTracer->registerDataSource(); }, [&]() { - auto tracingSession = getTracingSessionForTest(); + if (tracingSession) { + tracingSession->StopBlocking(); + } + tracingSession = getTracingSessionForTest(); tracingSession->StartBlocking(); }, [&]() { traceTimestamp(layerIds, layerIds.size()); }, diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index c4077dfbaa..9b2d4536cd 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -225,16 +225,19 @@ namespace scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) + sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback, + IVsyncTrackerCallback& vsyncTrackerCallback) : TestableScheduler(std::make_unique<android::mock::VsyncController>(), std::make_shared<android::mock::VSyncTracker>(), selectorPtr, - std::move(modulatorPtr), callback) {} + std::move(modulatorPtr), callback, vsyncTrackerCallback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, VsyncSchedule::TrackerPtr tracker, std::shared_ptr<RefreshRateSelector> selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) - : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) { + sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback, + IVsyncTrackerCallback& vsyncTrackerCallback) + : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr), + vsyncTrackerCallback) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplayInternal(displayId, std::move(selectorPtr), std::shared_ptr<VsyncSchedule>( @@ -400,7 +403,8 @@ public: } // namespace surfaceflinger::test // TODO(b/189053744) : Create a common test/mock library for surfaceflinger -class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback { +class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback, + private scheduler::IVsyncTrackerCallback { public: using HotplugEvent = SurfaceFlinger::HotplugEvent; @@ -610,8 +614,8 @@ public: mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)); scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool()); - mFlinger->postComposition(displayId, ftl::init::map(displayId, &frameTargeter), - mFdp.ConsumeIntegral<nsecs_t>()); + mFlinger->onCompositionPresented(displayId, ftl::init::map(displayId, &frameTargeter), + mFdp.ConsumeIntegral<nsecs_t>()); } mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); @@ -656,6 +660,7 @@ public: std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, scheduler::ISchedulerCallback* callback = nullptr, + scheduler::IVsyncTrackerCallback* vsyncTrackerCallback = nullptr, bool hasMultipleModes = false) { constexpr DisplayModeId kModeId60{0}; DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz)); @@ -678,7 +683,8 @@ public: mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), mRefreshRateSelector, - std::move(modulatorPtr), *(callback ?: this)); + std::move(modulatorPtr), *(callback ?: this), + *(vsyncTrackerCallback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); @@ -799,6 +805,10 @@ private: void triggerOnFrameRateOverridesChanged() override {} void onChoreographerAttached() override {} + // IVsyncTrackerCallback overrides + void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, + Period) override {} + surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp index 39a7ee5c12..7aae3c4a1a 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp @@ -133,7 +133,7 @@ void LayerFuzzer::invokeBufferStateLayer() { ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>())); layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>()); - layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming); + layer->onCompositionPresented(nullptr, fenceTime, fenceTime, compositorTiming); layer->setTransform(mFdp.ConsumeIntegral<uint32_t>()); layer->setTransformToDisplayInverse(mFdp.ConsumeBool()); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index a8727f966c..8fcfd8131a 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -178,14 +178,23 @@ void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp); } +struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback { + void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, + Period) override {} +}; + void SchedulerFuzzer::fuzzVSyncPredictor() { uint16_t now = mFdp.ConsumeIntegral<uint16_t>(); uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX); - scheduler::VSyncPredictor tracker{kDisplayId, idealPeriod, historySize, + VsyncTrackerCallback callback; + scheduler::VSyncPredictor tracker{kDisplayId, + idealPeriod, + historySize, minimumSamplesForPrediction, - mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/}; + mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/, + callback}; uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); tracker.setPeriod(period); for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) { diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 8061a8f2dc..728708f05c 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -100,7 +100,7 @@ public: return true; } - void setRenderRate(Fps) override {} + void setDisplayModeData(const scheduler::DisplayModeData&) override {} nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index 9889cb9c5d..6c8972f1fb 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -29,12 +29,12 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/ProcessState.h> +#include <common/FlagManager.h> #include <configstore/Utils.h> #include <displayservice/DisplayService.h> #include <errno.h> #include <hidl/LegacySupport.h> #include <processgroup/sched_policy.h> -#include "FlagManager.h" #include "SurfaceFlinger.h" #include "SurfaceFlingerFactory.h" #include "SurfaceFlingerProperties.h" diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index fec450e80d..bb3c94a4eb 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -61,3 +61,18 @@ flag { bug: "303460805" is_fixed_read_only: true } + +flag { + name: "hdcp_level_hal" + namespace: "core_graphics" + description: "Feature flag for adding a HAL API to commuicate hdcp levels" + bug: "285359126" + 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 9269e7c8a0..c9af432513 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -47,7 +47,6 @@ protected: ASSERT_NO_FATAL_FAILURE(SetUpDisplay()); sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); - mCaptureArgs.displayToken = mDisplay; } virtual void TearDown() { @@ -279,8 +278,6 @@ protected: 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 96cc333a08..79864e05af 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 @@ protected: 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 @@ protected: 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 @@ protected: .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 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { 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 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { { // 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 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { // 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 @@ TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) { 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 @@ TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) { // 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, CaptureLayerExclude) { 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 @@ TEST_F(ScreenCaptureTest, CaptureSecureLayer) { 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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) { 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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) { { // 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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) { .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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) { .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 @@ TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) { { // 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 @@ TEST_F(ScreenCaptureTest, CaptureOffscreen) { 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 @@ TEST_F(ScreenCaptureTest, CaptureOffscreen) { 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 d0ab105414..c5d118c1aa 100644 --- a/services/surfaceflinger/tests/TextureFiltering_test.cpp +++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp @@ -80,29 +80,22 @@ protected: 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, BufferCropNoFiltering) { 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 @@ TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) { // 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 @@ TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) { // 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, LayerCaptureOutputSourceCropNoFiltering) { 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 @@ TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) { // 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, OutputSourceCropDisplayFrameMatchNoFiltering) { 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, LayerCropDisplayFrameMatchNoFiltering) { 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, ParentCropNoFiltering) { 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/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 5a3bca115b..dea019431b 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -45,7 +45,7 @@ filegroup { cc_aconfig_library { name: "libsurfaceflingerflags_test", aconfig_declarations: "surfaceflinger_flags", - test: true, + mode: "test", } cc_test { @@ -170,6 +170,7 @@ cc_defaults { "librenderengine_mocks", "libscheduler", "libserviceutils", + "libsurfaceflinger_common_test", "libtimestats", "libtimestats_atoms_proto", "libtimestats_proto", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 8e13c0d9df..ee967979cd 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -347,7 +347,7 @@ struct BaseDisplayVariant { } 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 @@ struct BaseDisplayVariant { } 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/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index fa31643df1..13796650c8 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -80,7 +80,8 @@ void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) { std::unique_ptr<EventThread>(mEventThread), std::unique_ptr<EventThread>(mSFEventThread), TestableSurfaceFlinger::DefaultDisplayMode{displayId}, - TestableSurfaceFlinger::SchedulerCallbackImpl::kMock); + TestableSurfaceFlinger::SchedulerCallbackImpl::kMock, + TestableSurfaceFlinger::VsyncTrackerCallbackImpl::kMock); } void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) { diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp index aa37754300..c040f29fec 100644 --- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp @@ -17,7 +17,7 @@ #undef LOG_TAG #define LOG_TAG "FlagManagerTest" -#include "FlagManager.h" +#include <common/FlagManager.h> #include "FlagUtils.h" #include <gmock/gmock.h> diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/tests/unittests/FlagUtils.h index 333e4e7e05..550c70d98f 100644 --- a/services/surfaceflinger/tests/unittests/FlagUtils.h +++ b/services/surfaceflinger/tests/unittests/FlagUtils.h @@ -16,7 +16,7 @@ #pragma once -#include "FlagManager.h" +#include <common/FlagManager.h> #define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value)) diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 636d852651..e0400280fe 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 @@ void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& sour 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/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 4f545a9ef3..f1e841b88f 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -30,6 +30,7 @@ #include <gmock/gmock.h> #pragma clang diagnostic pop +#include <common/FlagManager.h> #include <gui/LayerMetadata.h> #include <log/log.h> #include <chrono> @@ -38,9 +39,12 @@ #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/Hal.h" #include "DisplayIdentificationTestHelpers.h" +#include "FlagUtils.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockHWC2.h" +#include <com_android_graphics_surfaceflinger_flags.h> + // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" @@ -57,7 +61,6 @@ using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugDat using hal::IComposerClient; using ::testing::_; using ::testing::DoAll; -using ::testing::ElementsAreArray; using ::testing::Return; using ::testing::SetArgPointee; using ::testing::StrictMock; @@ -79,10 +82,13 @@ struct HWComposerTest : testing::Test { EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId)); } - void setDisplayData(HalDisplayId displayId, nsecs_t lastExpectedPresentTimestamp) { + void setDisplayData(HalDisplayId displayId, TimePoint lastExpectedPresentTimestamp, + Fps lastFrameInterval) { ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end()); auto& displayData = mHwc.mDisplayData.at(displayId); + std::scoped_lock lock{displayData.expectedPresentLock}; displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp; + displayData.lastFrameInterval = lastFrameInterval; } }; @@ -214,7 +220,102 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { } } -TEST_F(HWComposerTest, getModesWithDisplayConfigurations) { +TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) { + // if vrr_config is off, getDisplayConfigurationsSupported() is off as well + // then getModesWithLegacyDisplayConfigs should be called instead + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, false); + ASSERT_FALSE(FlagManager::getInstance().vrr_config()); + + constexpr hal::HWDisplayId kHwcDisplayId = 2; + constexpr hal::HWConfigId kConfigId = 42; + constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps + + expectHotplugConnect(kHwcDisplayId); + const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + ASSERT_TRUE(info); + + EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false)); + + { + EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _)) + .WillOnce(Return(HalError::BAD_DISPLAY)); + EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty()); + } + { + constexpr int32_t kWidth = 480; + constexpr int32_t kHeight = 720; + constexpr int32_t kConfigGroup = 1; + constexpr int32_t kVsyncPeriod = 16666667; + + EXPECT_CALL(*mHal, + getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH, + _)) + .WillRepeatedly(DoAll(SetArgPointee<3>(kWidth), Return(HalError::NONE))); + EXPECT_CALL(*mHal, + getDisplayAttribute(kHwcDisplayId, kConfigId, + IComposerClient::Attribute::HEIGHT, _)) + .WillRepeatedly(DoAll(SetArgPointee<3>(kHeight), Return(HalError::NONE))); + EXPECT_CALL(*mHal, + getDisplayAttribute(kHwcDisplayId, kConfigId, + IComposerClient::Attribute::CONFIG_GROUP, _)) + .WillRepeatedly(DoAll(SetArgPointee<3>(kConfigGroup), Return(HalError::NONE))); + EXPECT_CALL(*mHal, + getDisplayAttribute(kHwcDisplayId, kConfigId, + IComposerClient::Attribute::VSYNC_PERIOD, _)) + .WillRepeatedly(DoAll(SetArgPointee<3>(kVsyncPeriod), Return(HalError::NONE))); + + // Optional Parameters UNSUPPORTED + EXPECT_CALL(*mHal, + getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X, + _)) + .WillOnce(Return(HalError::UNSUPPORTED)); + EXPECT_CALL(*mHal, + getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y, + _)) + .WillOnce(Return(HalError::UNSUPPORTED)); + + EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{kConfigId}), + Return(HalError::NONE))); + + auto modes = mHwc.getModes(info->id, kMaxFrameIntervalNs); + EXPECT_EQ(modes.size(), size_t{1}); + EXPECT_EQ(modes.front().hwcId, kConfigId); + EXPECT_EQ(modes.front().width, kWidth); + EXPECT_EQ(modes.front().height, kHeight); + EXPECT_EQ(modes.front().configGroup, kConfigGroup); + EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod); + EXPECT_EQ(modes.front().dpiX, -1); + EXPECT_EQ(modes.front().dpiY, -1); + + // Optional parameters are supported + constexpr int32_t kDpi = 320; + EXPECT_CALL(*mHal, + getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X, + _)) + .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE))); + EXPECT_CALL(*mHal, + getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y, + _)) + .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE))); + + modes = mHwc.getModes(info->id, kMaxFrameIntervalNs); + EXPECT_EQ(modes.size(), size_t{1}); + EXPECT_EQ(modes.front().hwcId, kConfigId); + EXPECT_EQ(modes.front().width, kWidth); + EXPECT_EQ(modes.front().height, kHeight); + EXPECT_EQ(modes.front().configGroup, kConfigGroup); + EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod); + // DPI values are scaled by 1000 in the legacy implementation. + EXPECT_EQ(modes.front().dpiX, kDpi / 1000.f); + EXPECT_EQ(modes.front().dpiY, kDpi / 1000.f); + } +} + +TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true); + ASSERT_TRUE(FlagManager::getInstance().vrr_config()); + constexpr hal::HWDisplayId kHwcDisplayId = 2; constexpr hal::HWConfigId kConfigId = 42; constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps @@ -322,48 +423,137 @@ TEST_F(HWComposerTest, notifyExpectedPresentTimeout) { ASSERT_TRUE(info); auto expectedPresentTime = systemTime() + ms2ns(10); - const int32_t frameIntervalNs = static_cast<Fps>(60_Hz).getPeriodNsecs(); - static constexpr nsecs_t kTimeoutNs = ms2ns(30); + static constexpr Fps Fps60Hz = 60_Hz; + static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameInterval60HzNs = Fps60Hz.getPeriodNsecs(); + static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); + static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0); - ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, /* lastExpectedPresentTimestamp= */ 0)); + ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, kLastExpectedPresentTimestamp, Fps60Hz)); { // Very first ExpectedPresent after idle, no previous timestamp EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { + // Absent timeoutNs + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + /*timeoutOpt*/ std::nullopt); + } + { + // Timeout is 0 + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mHal, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(HalError::NONE)); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + Period::fromNs(0)); + } + { // ExpectedPresent is after the timeoutNs - expectedPresentTime += ms2ns(50); + expectedPresentTime += 2 * kFrameInterval5HzNs; EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent has not changed + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { // ExpectedPresent is after the last reported ExpectedPresent. - expectedPresentTime += ms2ns(10); + expectedPresentTime += kFrameInterval60HzNs; EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, // representing we changed our decision and want to present earlier than previously // reported. - expectedPresentTime -= ms2ns(20); + expectedPresentTime -= kFrameInterval120HzNs; EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } } +TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) { + constexpr hal::HWDisplayId kHwcDisplayId = 2; + expectHotplugConnect(kHwcDisplayId); + const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + ASSERT_TRUE(info); + + const auto now = systemTime(); + auto expectedPresentTime = now; + static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); + + ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, TimePoint::fromNs(now), Fps::fromValue(0))); + static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + + struct FrameRateIntervalTestData { + int32_t frameIntervalNs; + bool callExpectedPresent; + }; + const std::vector<FrameRateIntervalTestData> frameIntervals = { + {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, + {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true}, + {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true}, + {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false}, + {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true}, + {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true}, + {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, + }; + + for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { + { + expectedPresentTime += frameIntervalNs; + if (callExpectedPresent) { + EXPECT_CALL(*mHal, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, + frameIntervalNs)) + .WillOnce(Return(HalError::NONE)); + } else { + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + } + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), + Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); + } + } +} + struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection)); MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId)); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 2f6058f7a6..befef482b9 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -165,8 +165,10 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; + mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback); + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); TestableSurfaceFlinger mFlinger; }; @@ -837,6 +839,81 @@ TEST_F(LayerHistoryIntegrationTest, DISABLED_smallDirtyInMultiLayer) { 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/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index e8831ab51f..1adf14f587 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -144,7 +144,9 @@ protected: mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback); + mock::VsyncTrackerCallback mVsyncTrackerCallback; + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); TestableSurfaceFlinger mFlinger; }; @@ -925,6 +927,43 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { EXPECT_EQ(1, animatingLayerCount(time)); } +TEST_F(LayerHistoryTest, frontBufferedLayerVotesMax) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + auto layer = createLayer(); + + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + EXPECT_CALL(*layer, isFrontBuffered()).WillRepeatedly(Return(true)); + + nsecs_t time = systemTime(); + + EXPECT_EQ(1, layerCount()); + EXPECT_EQ(0, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // layer is active but infrequent. + for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + } + + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // layer became inactive + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); +} + TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { auto layer = createLayer(); diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index 11072bc4c9..047ef5a928 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -21,6 +21,7 @@ #include <scheduler/Fps.h> +#include "FlagUtils.h" #include "FpsOps.h" #include "Scheduler/LayerHistory.h" #include "Scheduler/LayerInfo.h" @@ -28,6 +29,8 @@ #include "TestableSurfaceFlinger.h" #include "mock/MockSchedulerCallback.h" +#include <com_android_graphics_surfaceflinger_flags.h> + namespace android::scheduler { using android::mock::createDisplayMode; @@ -61,12 +64,16 @@ protected: HI_FPS)), DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback); + mock::VsyncTrackerCallback mVsyncTrackerCallback; + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); TestableSurfaceFlinger mFlinger; }; namespace { +using namespace com::android::graphics::surfaceflinger; + TEST_F(LayerInfoTest, prefersPresentTime) { std::deque<FrameTimeData> frameTimes; constexpr auto kExpectedFps = 50_Hz; @@ -261,5 +268,18 @@ TEST_F(LayerInfoTest, getRefreshRateVote_noData) { ASSERT_EQ(actualVotes[0].fps, vote.fps); } +TEST_F(LayerInfoTest, isFrontBuffered) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + ASSERT_FALSE(layerInfo.isFrontBuffered()); + + LayerProps prop = {.isFrontBuffered = true}; + layerInfo.setLastPresentTime(0, 0, LayerHistory::LayerUpdateType::Buffer, true, prop); + ASSERT_TRUE(layerInfo.isFrontBuffered()); + + prop.isFrontBuffered = false; + layerInfo.setLastPresentTime(0, 0, LayerHistory::LayerUpdateType::Buffer, true, prop); + ASSERT_FALSE(layerInfo.isFrontBuffered()); +} + } // namespace } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index e784eb76f3..2cc6dc7721 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -789,6 +789,143 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategy) { 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) { @@ -898,4 +1035,24 @@ TEST_F(LayerSnapshotTest, setTrustedOverlayForNonVisibleInput) { gui::WindowInfo::InputConfig::TRUSTED_OVERLAY)); } +TEST_F(LayerSnapshotTest, isFrontBuffered) { + setBuffer(1, + std::make_shared<renderengine::mock::FakeExternalTexture>( + 1U /*width*/, 1U /*height*/, 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_HW_TEXTURE | AHARDWAREBUFFER_USAGE_FRONT_BUFFER /*usage*/)); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->isFrontBuffered()); + + setBuffer(1, + std::make_shared< + renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, + 1ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_HW_TEXTURE /*usage*/)); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->isFrontBuffered()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index faa12a1032..a00e8864dd 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1568,7 +1568,7 @@ TEST_P(RefreshRateSelectorTest, // These layers cannot change mode due to smoothSwitchOnly, and will definitely use // active mode (120Hz). {FrameRateCategory::NoPreference, true, 120_Hz, kModeId120}, - {FrameRateCategory::Low, true, 40_Hz, kModeId120}, + {FrameRateCategory::Low, true, 120_Hz, kModeId120}, {FrameRateCategory::Normal, true, 40_Hz, kModeId120}, {FrameRateCategory::High, true, 120_Hz, kModeId120}, }; diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 87fae2c312..b5eb777a4d 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -89,7 +89,9 @@ protected: kDisplay1Mode60->getId()); mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback}; + mock::VsyncTrackerCallback mVsyncTrackerCallback; + TestableScheduler* mScheduler = + new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback}; surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}}; ConnectionHandle mConnectionHandle; diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index d0290ea03e..b80cb6625f 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -103,7 +103,7 @@ TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { 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 @@ TEST_F(SurfaceFlingerPowerHintTest, inactiveOnDisplayDoze) { 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/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 3d1c900dcf..8b6f0f1b94 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -36,18 +36,21 @@ namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback) + TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback, + IVsyncTrackerCallback& vsyncTrackerCallback) : TestableScheduler(std::make_unique<mock::VsyncController>(), std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr), - sp<VsyncModulator>::make(VsyncConfigSet{}), callback) {} + sp<VsyncModulator>::make(VsyncConfigSet{}), callback, + vsyncTrackerCallback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) - : Scheduler(*this, callback, + sp<VsyncModulator> modulatorPtr, ISchedulerCallback& schedulerCallback, + IVsyncTrackerCallback& vsyncTrackerCallback) + : Scheduler(*this, schedulerCallback, (FeatureFlags)Feature::kContentDetection | Feature::kSmallDirtyContentDetection, - std::move(modulatorPtr)) { + std::move(modulatorPtr), vsyncTrackerCallback) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplay(displayId, std::move(selectorPtr), std::move(controller), std::move(tracker)); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 03af56cc71..bca14f5217 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -53,6 +53,7 @@ #include "mock/MockFrameTimeline.h" #include "mock/MockFrameTracer.h" #include "mock/MockSchedulerCallback.h" +#include "mock/MockVsyncTrackerCallback.h" #include "mock/system/window/MockNativeWindow.h" #include "Scheduler/VSyncTracker.h" @@ -204,6 +205,8 @@ public: enum class SchedulerCallbackImpl { kNoOp, kMock }; + enum class VsyncTrackerCallbackImpl { kNoOp, kMock }; + struct DefaultDisplayMode { // The ID of the injected RefreshRateSelector and its default display mode. PhysicalDisplayId displayId; @@ -213,13 +216,14 @@ public: using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>; - void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, - std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, - std::unique_ptr<EventThread> appEventThread, - std::unique_ptr<EventThread> sfEventThread, - DisplayModesVariant modesVariant, - SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, - bool useNiceMock = false) { + void setupScheduler( + std::unique_ptr<scheduler::VsyncController> vsyncController, + std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, + std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, + DisplayModesVariant modesVariant, + SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, + VsyncTrackerCallbackImpl vsyncTrackerCallbackImpl = VsyncTrackerCallbackImpl::kNoOp, + bool useNiceMock = false) { RefreshRateSelectorPtr selectorPtr = ftl::match( modesVariant, [](DefaultDisplayMode arg) { @@ -239,10 +243,16 @@ public: mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); - using Callback = scheduler::ISchedulerCallback; - Callback& callback = callbackImpl == SchedulerCallbackImpl::kNoOp - ? static_cast<Callback&>(mNoOpSchedulerCallback) - : static_cast<Callback&>(mSchedulerCallback); + using ISchedulerCallback = scheduler::ISchedulerCallback; + ISchedulerCallback& schedulerCallback = callbackImpl == SchedulerCallbackImpl::kNoOp + ? static_cast<ISchedulerCallback&>(mNoOpSchedulerCallback) + : static_cast<ISchedulerCallback&>(mSchedulerCallback); + + using VsyncTrackerCallback = scheduler::IVsyncTrackerCallback; + VsyncTrackerCallback& vsyncTrackerCallback = + vsyncTrackerCallbackImpl == VsyncTrackerCallbackImpl::kNoOp + ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback) + : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback); auto modulatorPtr = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); @@ -253,12 +263,14 @@ public: std::move(vsyncTracker), std::move(selectorPtr), std::move(modulatorPtr), - callback); + schedulerCallback, + vsyncTrackerCallback); } else { mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(selectorPtr), - std::move(modulatorPtr), callback); + std::move(modulatorPtr), + schedulerCallback, vsyncTrackerCallback); } mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms); @@ -297,7 +309,8 @@ public: EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), DefaultDisplayMode{options.displayId}, - SchedulerCallbackImpl::kNoOp, options.useNiceMock); + SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp, + options.useNiceMock); } void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } @@ -610,6 +623,9 @@ public: 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); @@ -1071,6 +1087,8 @@ private: sp<SurfaceFlinger> mFlinger; scheduler::mock::SchedulerCallback mSchedulerCallback; scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback; + scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; + scheduler::mock::NoOpVsyncTrackerCallback mNoOpVsyncTrackerCallback; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; scheduler::TestableScheduler* mScheduler = nullptr; Hwc2::mock::PowerAdvisor mPowerAdvisor; diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 00b5bf0506..d4d5b32341 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -112,7 +112,7 @@ public: EXPECT_CALL(*mFlinger.getFrameTracer(), traceFence(layerId, bufferId, frameNumber, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0)); - layer->onPostComposition(nullptr, glDoneFence, presentFence, compositorTiming); + layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming); } }; diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 41866a1c50..4be07a1ddb 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -54,7 +54,7 @@ public: void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } - void setRenderRate(Fps) final {} + void setDisplayModeData(const DisplayModeData&) final {} void dump(std::string&) const final {} private: @@ -92,7 +92,7 @@ public: void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } - void setRenderRate(Fps) final {} + void setDisplayModeData(const DisplayModeData&) final {} void dump(std::string&) const final {} private: diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 1dc5498221..83108662b9 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -59,7 +59,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setRenderRate, (Fps), (override)); + MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const { @@ -783,7 +783,9 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipASchedule TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTargetVSync) { SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); - EXPECT_CALL(mMockClock, alarmAt(_, 500)); + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 400)).InSequence(seq); CountingCallback cb(mDispatch); auto result = mDispatch->schedule(cb, @@ -873,7 +875,9 @@ TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) { SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); - EXPECT_CALL(mMockClock, alarmAt(_, 600)); + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq); CountingCallback cb(mDispatch); auto result = @@ -1119,6 +1123,7 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq); CountingCallback cb(mDispatch); @@ -1132,7 +1137,7 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { ASSERT_THAT(cb.mCalls.size(), Eq(1)); EXPECT_THAT(cb.mCalls[0], Eq(1000)); ASSERT_THAT(cb.mWakeupTime.size(), Eq(1)); - EXPECT_THAT(cb.mWakeupTime[0], Eq(600)); + EXPECT_THAT(cb.mWakeupTime[0], Eq(0)); ASSERT_THAT(cb.mReadyTime.size(), Eq(1)); EXPECT_THAT(cb.mReadyTime[0], Eq(1000)); } @@ -1161,7 +1166,9 @@ TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) { TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) { SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); - EXPECT_CALL(mMockClock, alarmAt(_, 500)); + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 300)).InSequence(seq); CountingCallback cb(mDispatch); auto result = mDispatch->schedule(cb, @@ -1177,6 +1184,11 @@ TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) { advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); + EXPECT_THAT(cb.mCalls[0], Eq(1000)); + ASSERT_THAT(cb.mWakeupTime.size(), Eq(1)); + EXPECT_THAT(cb.mWakeupTime[0], Eq(300)); + ASSERT_THAT(cb.mReadyTime.size(), Eq(1)); + EXPECT_THAT(cb.mReadyTime[0], Eq(1000)); } class VSyncDispatchTimerQueueEntryTest : public testing::Test { diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 43d683d0fa..30a2855955 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -23,7 +23,9 @@ #define LOG_TAG "LibSurfaceFlingerUnittests" #define LOG_NDEBUG 0 +#include "FlagUtils.h" #include "Scheduler/VSyncPredictor.h" +#include "mock/MockVsyncTrackerCallback.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -31,8 +33,11 @@ #include <chrono> #include <utility> +#include <com_android_graphics_surfaceflinger_flags.h> + using namespace testing; using namespace std::literals; +using namespace com::android::graphics::surfaceflinger; namespace android::scheduler { @@ -52,13 +57,18 @@ constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u struct VSyncPredictorTest : testing::Test { nsecs_t mNow = 0; nsecs_t mPeriod = 1000; + scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; static constexpr size_t kHistorySize = 10; static constexpr size_t kMinimumSamplesForPrediction = 6; static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; - VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent}; + VSyncPredictor tracker{DEFAULT_DISPLAY_ID, + mPeriod, + kHistorySize, + kMinimumSamplesForPrediction, + kOutlierTolerancePercent, + mVsyncTrackerCallback}; }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { @@ -378,8 +388,12 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { - VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, 20, kMinimumSamplesForPrediction, - kOutlierTolerancePercent}; + VSyncPredictor tracker{DEFAULT_DISPLAY_ID, + mPeriod, + 20, + kMinimumSamplesForPrediction, + kOutlierTolerancePercent, + mVsyncTrackerCallback}; std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675, 840923581635, 840940161584, 840956868096, 840973702473, 840990256277, 841007116851, @@ -566,7 +580,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsRespected) { tracker.addVsyncTimestamp(mNow); } - tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod)); + tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3 * mPeriod)}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -588,12 +602,12 @@ TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - tracker.setRenderRate(refreshRate / 4); + tracker.setDisplayModeData({.renderRate = refreshRate / 4}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod)); - tracker.setRenderRate(refreshRate / 2); + tracker.setDisplayModeData({.renderRate = refreshRate / 2}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod)); @@ -601,7 +615,7 @@ TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod)); - tracker.setRenderRate(refreshRate / 6); + tracker.setDisplayModeData({.renderRate = refreshRate / 6}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod)); } @@ -615,7 +629,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { tracker.addVsyncTimestamp(mNow); } - tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod)); + tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3.5f * mPeriod)}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -626,6 +640,39 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } +TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); + DisplayModeData displayModeData = + DisplayModeData{.renderRate = refreshRate, + .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)}; + tracker.setDisplayModeData(displayModeData); + auto last = mNow; + for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { + EXPECT_CALL(mVsyncTrackerCallback, + onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(last + mPeriod), + displayModeData, Period::fromNs(mPeriod))) + .Times(1); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); + mNow += mPeriod; + last = mNow; + tracker.addVsyncTimestamp(mNow); + } + + displayModeData = DisplayModeData{.renderRate = refreshRate / 2, + .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)}; + tracker.setDisplayModeData(displayModeData); + { + // out of render rate phase + EXPECT_CALL(mVsyncTrackerCallback, + onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(mNow + 3 * mPeriod), + displayModeData, Period::fromNs(mPeriod))) + .Times(1); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), + Eq(mNow + 3 * mPeriod)); + } +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 122192b036..aca3ccca6d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -50,7 +50,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setRenderRate, (Fps), (override)); + MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 0b0774551a..3b74f0ab4a 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -95,9 +95,9 @@ public: 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 40f59b8cb9..a7ddb6df58 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -76,9 +76,9 @@ public: (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)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index 4cc78febee..3dfb649a02 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -37,6 +37,7 @@ public: MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility()); MOCK_CONST_METHOD0(getOwnerUid, uid_t()); MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace()); + MOCK_METHOD(bool, isFrontBuffered, (), (const, override)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index dcf25e18a8..31eb86e4c5 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -34,7 +34,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setRenderRate, (Fps), (override)); + MOCK_METHOD(void, setDisplayModeData, (const scheduler::DisplayModeData&), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h new file mode 100644 index 0000000000..b8e24e0593 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h @@ -0,0 +1,35 @@ +/* + * 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 <gmock/gmock.h> + +#include "Scheduler/VSyncTracker.h" + +namespace android::scheduler::mock { + +struct VsyncTrackerCallback final : IVsyncTrackerCallback { + MOCK_METHOD(void, onVsyncGenerated, + (PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, Period), + (override)); +}; + +struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback { + void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, + Period) override{}; +}; +} // namespace android::scheduler::mock diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 5d7a4aa170..cae51a5ba8 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -183,8 +183,12 @@ int LoadDriver(android_namespace_t* library_namespace, .library_namespace = library_namespace, }; so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo); - ALOGE("Could not load %s from updatable gfx driver namespace: %s.", - lib_name.c_str(), dlerror()); + if (!so) { + ALOGE( + "Could not load %s from updatable gfx driver namespace: " + "%s.", + lib_name.c_str(), dlerror()); + } } else { // load built-in driver so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS); |