diff options
175 files changed, 6669 insertions, 1472 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/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp index 55326ea737..de0e1718ab 100644 --- a/cmds/sfdo/sfdo.cpp +++ b/cmds/sfdo/sfdo.cpp @@ -16,7 +16,7 @@ #include <inttypes.h> #include <stdint.h> #include <any> -#include <unordered_map> +#include <map> #include <cutils/properties.h> #include <sys/resource.h> @@ -29,18 +29,28 @@ using namespace android; -std::unordered_map<std::string, std::any> g_functions; +std::map<std::string, std::any> g_functions; -const std::unordered_map<std::string, std::string> g_function_details = { - {"DebugFlash", "[optional(delay)] Perform a debug flash."}, - {"FrameRateIndicator", "[hide | show] displays the framerate in the top left corner."}, - {"scheduleComposite", "Force composite ahead of next VSYNC."}, - {"scheduleCommit", "Force commit ahead of next VSYNC."}, - {"scheduleComposite", "PENDING - if you have a good understanding let me know!"}, +enum class ParseToggleResult { + kError, + kFalse, + kTrue, +}; + +const std::map<std::string, std::string> g_function_details = { + {"debugFlash", "[optional(delay)] Perform a debug flash."}, + {"frameRateIndicator", "[hide | show] displays the framerate in the top left corner."}, + {"scheduleComposite", "Force composite ahead of next VSYNC."}, + {"scheduleCommit", "Force commit ahead of next VSYNC."}, + {"scheduleComposite", "PENDING - if you have a good understanding let me know!"}, + {"forceClientComposition", + "[enabled | disabled] When enabled, it disables " + "Hardware Overlays, and routes all window composition to the GPU. This can " + "help check if there is a bug in HW Composer."}, }; static void ShowUsage() { - std::cout << "usage: sfdo [help, FrameRateIndicator show, DebugFlash enabled, ...]\n\n"; + std::cout << "usage: sfdo [help, frameRateIndicator show, debugFlash enabled, ...]\n\n"; for (const auto& sf : g_functions) { const std::string fn = sf.first; std::string fdetails = "TODO"; @@ -50,7 +60,26 @@ static void ShowUsage() { } } -int FrameRateIndicator([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { +// Returns 1 for positive keywords and 0 for negative keywords. +// If the string does not match any it will return -1. +ParseToggleResult parseToggle(const char* str) { + const std::unordered_set<std::string> positive{"1", "true", "y", "yes", + "on", "enabled", "show"}; + const std::unordered_set<std::string> negative{"0", "false", "n", "no", + "off", "disabled", "hide"}; + + const std::string word(str); + if (positive.count(word)) { + return ParseToggleResult::kTrue; + } + if (negative.count(word)) { + return ParseToggleResult::kFalse; + } + + return ParseToggleResult::kError; +} + +int frameRateIndicator(int argc, char** argv) { bool hide = false, show = false; if (argc == 3) { show = strcmp(argv[2], "show") == 0; @@ -60,13 +89,13 @@ int FrameRateIndicator([[maybe_unused]] int argc, [[maybe_unused]] char** argv) if (show || hide) { ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show); } else { - std::cerr << "Incorrect usage of FrameRateIndicator. Missing [hide | show].\n"; + std::cerr << "Incorrect usage of frameRateIndicator. Missing [hide | show].\n"; return -1; } return 0; } -int DebugFlash([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { +int debugFlash(int argc, char** argv) { int delay = 0; if (argc == 3) { delay = atoi(argv[2]) == 0; @@ -86,14 +115,40 @@ int scheduleCommit([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { return 0; } +int forceClientComposition(int argc, char** argv) { + bool enabled = true; + // A valid command looks like this: + // adb shell sfdo forceClientComposition enabled + if (argc >= 3) { + const ParseToggleResult toggle = parseToggle(argv[2]); + if (toggle == ParseToggleResult::kError) { + std::cerr << "Incorrect usage of forceClientComposition. " + "Missing [enabled | disabled].\n"; + return -1; + } + if (argc > 3) { + std::cerr << "Too many arguments after [enabled | disabled]. " + "Ignoring extra arguments.\n"; + } + enabled = (toggle == ParseToggleResult::kTrue); + } else { + std::cerr << "Incorrect usage of forceClientComposition. Missing [enabled | disabled].\n"; + return -1; + } + + ComposerServiceAIDL::getComposerService()->forceClientComposition(enabled); + return 0; +} + int main(int argc, char** argv) { std::cout << "Execute SurfaceFlinger internal commands.\n"; std::cout << "sfdo requires to be run with root permissions..\n"; - g_functions["FrameRateIndicator"] = FrameRateIndicator; - g_functions["DebugFlash"] = DebugFlash; + g_functions["frameRateIndicator"] = frameRateIndicator; + g_functions["debugFlash"] = debugFlash; g_functions["scheduleComposite"] = scheduleComposite; g_functions["scheduleCommit"] = scheduleCommit; + g_functions["forceClientComposition"] = forceClientComposition; if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) { std::cout << "Running: " << argv[1] << "\n"; 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.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/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/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 824323857d..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> @@ -308,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)); @@ -316,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; } } @@ -340,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; } } @@ -377,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: { @@ -414,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."); } } } @@ -705,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); @@ -723,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__ } @@ -752,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/FdTrigger.cpp b/libs/binder/FdTrigger.cpp index a1fbbf321c..895690f9b8 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -53,7 +53,7 @@ bool FdTrigger::isTriggered() { #ifdef BINDER_RPC_SINGLE_THREADED return mTriggered; #else - return mWrite == -1; + return !mWrite.ok(); #endif } diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp index cedd3af289..f7b5a55a31 100644 --- a/libs/binder/RecordedTransaction.cpp +++ b/libs/binder/RecordedTransaction.cpp @@ -15,10 +15,10 @@ */ #include <android-base/file.h> -#include <android-base/logging.h> #include <android-base/unique_fd.h> #include <binder/Functional.h> #include <binder/RecordedTransaction.h> +#include <inttypes.h> #include <sys/mman.h> #include <algorithm> @@ -127,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; } @@ -168,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; } @@ -207,12 +205,12 @@ 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; } @@ -228,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; @@ -237,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); @@ -265,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; @@ -273,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; @@ -281,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); @@ -292,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, @@ -321,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; @@ -331,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 1ba20b3103..fefaa810d2 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -168,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); } @@ -200,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; diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index c895b21f91..cd8f41711f 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -208,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; @@ -594,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/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/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 c8431aab43..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,8 +24,16 @@ #include <log/log.h> #include <utils/Errors.h> -#define PLOGE_VA_ARGS(...) , ##__VA_ARGS__ -#define PLOGE(fmt, ...) ALOGE(fmt ": %s" PLOGE_VA_ARGS(__VA_ARGS__), strerror(errno)) +#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 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 78f8877c1d..6d122c5388 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -27,7 +27,7 @@ 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; @@ -62,7 +62,7 @@ pub trait Interface: Send + Sync + DowncastSync { /// /// 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(()) } } @@ -165,7 +165,7 @@ pub trait Remotable: Send + Sync + 'static { /// 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. /// @@ -934,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 { 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/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/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/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 88a1075f60..0000000000 --- a/libs/binder/trusty/logging.cpp +++ /dev/null @@ -1,165 +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/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/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/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a3518110cd..922b0ddcde 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -3122,12 +3122,12 @@ status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, return statusTFromBinderStatus(status); } -status_t ScreenshotClient::captureDisplay(DisplayId displayId, +status_t ScreenshotClient::captureDisplay(DisplayId displayId, const gui::CaptureArgs& captureArgs, const sp<IScreenCaptureListener>& captureListener) { sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService()); if (s == nullptr) return NO_INIT; - binder::Status status = s->captureDisplayById(displayId.value, captureListener); + binder::Status status = s->captureDisplayById(displayId.value, captureArgs, captureListener); return statusTFromBinderStatus(status); } 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/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl new file mode 100644 index 0000000000..920d94980a --- /dev/null +++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h"; diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index a7cf5ddeb4..d24f8eefd5 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -16,6 +16,7 @@ package android.gui; +import android.gui.CaptureArgs; import android.gui.Color; import android.gui.CompositionPreference; import android.gui.ContentSamplingAttributes; @@ -238,7 +239,8 @@ interface ISurfaceComposer { * Capture the specified screen. This requires the READ_FRAME_BUFFER * permission. */ - oneway void captureDisplayById(long displayId, IScreenCaptureListener listener); + oneway void captureDisplayById(long displayId, in CaptureArgs args, + IScreenCaptureListener listener); /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. @@ -514,6 +516,13 @@ interface ISurfaceComposer { void scheduleCommit(); /** + * Force all window composition to the GPU (i.e. disable Hardware Overlays). + * This can help check if there is a bug in HW Composer. + * Requires root or android.permission.HARDWARE_TEST + */ + void forceClientComposition(boolean enabled); + + /** * Gets priority of the RenderEngine in SurfaceFlinger. */ int getGpuContextPriority(); 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/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h index 3142103d17..ffe7e4123b 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -100,8 +100,8 @@ public: MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override)); MOCK_METHOD(binder::Status, captureDisplay, (const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); - MOCK_METHOD(binder::Status, captureDisplayById, (int64_t, const sp<IScreenCaptureListener>&), - (override)); + MOCK_METHOD(binder::Status, captureDisplayById, + (int64_t, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&), (override)); MOCK_METHOD(binder::Status, captureLayers, (const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override)); @@ -154,6 +154,7 @@ public: MOCK_METHOD(binder::Status, setDebugFlash, (int), (override)); MOCK_METHOD(binder::Status, scheduleComposite, (), (override)); MOCK_METHOD(binder::Status, scheduleCommit, (), (override)); + MOCK_METHOD(binder::Status, forceClientComposition, (bool), (override)); MOCK_METHOD(binder::Status, updateSmallAreaDetection, (const std::vector<int32_t>&, const std::vector<float>&), (override)); MOCK_METHOD(binder::Status, setSmallAreaDetectionThreshold, (int32_t, float), (override)); 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/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 54c3aa7c1c..5bf6c473d9 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -841,8 +841,14 @@ private: class ScreenshotClient { public: static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); - static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&); + static status_t captureDisplay(DisplayId, const gui::CaptureArgs&, + const sp<IScreenCaptureListener>&); static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); + + [[deprecated]] static status_t captureDisplay(DisplayId id, + const sp<IScreenCaptureListener>& listener) { + return captureDisplay(id, gui::CaptureArgs(), listener); + } }; // --------------------------------------------------------------------------- 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 86ced2ccf2..60221aa30a 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; @@ -851,7 +786,8 @@ public: return binder::Status::ok(); } - binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override { + binder::Status captureDisplayById(int64_t, const gui::CaptureArgs&, + const sp<IScreenCaptureListener>&) override { return binder::Status::ok(); } @@ -999,6 +935,10 @@ public: binder::Status scheduleCommit() override { return binder::Status::ok(); } + binder::Status forceClientComposition(bool /*enabled*/) override { + return binder::Status::ok(); + } + binder::Status updateSmallAreaDetection(const std::vector<int32_t>& /*appIds*/, const std::vector<float>& /*thresholds*/) { return binder::Status::ok(); 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 c37db1693a..dd8dc8dc9d 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -50,7 +50,7 @@ cc_aconfig_library { // 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, }, 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/permission/Android.bp b/libs/permission/Android.bp index 86dcaefd87..0eeca5469e 100644 --- a/libs/permission/Android.bp +++ b/libs/permission/Android.bp @@ -20,12 +20,6 @@ aidl_interface { ], } -filegroup { - name: "framework-permission-aidl-filegroup", - srcs: ["aidl/android/**/*.aidl"], - path: "aidl", -} - cc_library { name: "libpermission", host_supported: true, @@ -41,7 +35,6 @@ cc_library { "-Werror", ], srcs: [ - ":framework-permission-aidl-filegroup", "AppOpsManager.cpp", "IAppOpsCallback.cpp", "IAppOpsService.cpp", diff --git a/libs/permission/AppOpsManager.cpp b/libs/permission/AppOpsManager.cpp index b407d02087..695927418d 100644 --- a/libs/permission/AppOpsManager.cpp +++ b/libs/permission/AppOpsManager.cpp @@ -31,9 +31,6 @@ namespace android { -using ::android::String16; -using ::android::String8; - static const sp<IBinder>& getClientId() { static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER; static sp<IBinder> gClientId; @@ -46,11 +43,6 @@ static const sp<IBinder>& getClientId() { return gClientId; } - -static std::string getString(const String16& stringToConvert) { - return std::string(String8(stringToConvert).c_str()); -} - AppOpsManager::AppOpsManager() { } @@ -86,14 +78,9 @@ sp<IAppOpsService> AppOpsManager::getService() int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage) { sp<IAppOpsService> service = getService(); - if (service == nullptr) { - return AppOpsManager::MODE_IGNORED; - } - AttributionSourceState attributionSourceState; - attributionSourceState.uid = uid; - attributionSourceState.packageName = getString(callingPackage); - - return service->checkOperationWithState(op, attributionSourceState); + return service != nullptr + ? service->checkOperation(op, uid, callingPackage) + : AppOpsManager::MODE_IGNORED; } int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid, @@ -112,18 +99,12 @@ int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPa int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage, const std::optional<String16>& attributionTag, const String16& message) { sp<IAppOpsService> service = getService(); - if (service == nullptr) { - return AppOpsManager::MODE_IGNORED; - } - AttributionSourceState attributionSourceState; - attributionSourceState.uid = uid; - attributionSourceState.packageName = getString(callingPackage); - if (attributionTag.has_value()) { - attributionSourceState.attributionTag = getString(attributionTag.value()); - } + int32_t mode = service != nullptr + ? service->noteOperation(op, uid, callingPackage, attributionTag, + shouldCollectNotes(op), message, uid == AID_SYSTEM) + : AppOpsManager::MODE_IGNORED; - return service->noteOperationWithState(op, attributionSourceState, - shouldCollectNotes(op), message, uid == AID_SYSTEM); + return mode; } int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, @@ -136,18 +117,13 @@ int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& c bool startIfModeDefault, const std::optional<String16>& attributionTag, const String16& message) { sp<IAppOpsService> service = getService(); - if (service == nullptr) { - return AppOpsManager::MODE_IGNORED; - } - AttributionSourceState attributionSourceState; - attributionSourceState.uid = uid; - attributionSourceState.packageName = getString(callingPackage); - if (attributionTag.has_value()) { - attributionSourceState.attributionTag = getString(attributionTag.value()); - } + int32_t mode = service != nullptr + ? service->startOperation(getClientId(), op, uid, callingPackage, + attributionTag, startIfModeDefault, shouldCollectNotes(op), message, + uid == AID_SYSTEM) + : AppOpsManager::MODE_IGNORED; - return service->startOperationWithState(getClientId(), op, attributionSourceState, - startIfModeDefault,shouldCollectNotes(op), message, uid == AID_SYSTEM); + return mode; } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { @@ -157,16 +133,9 @@ void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPac void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage, const std::optional<String16>& attributionTag) { sp<IAppOpsService> service = getService(); - if (service == nullptr) { - return; - } - AttributionSourceState attributionSourceState; - attributionSourceState.uid = uid; - attributionSourceState.packageName = getString(callingPackage); - if (attributionTag.has_value()) { - attributionSourceState.attributionTag = getString(attributionTag.value()); + if (service != nullptr) { + service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag); } - service->finishOperationWithState(getClientId(), op, attributionSourceState); } void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName, diff --git a/libs/permission/IAppOpsService.cpp b/libs/permission/IAppOpsService.cpp index 33dd24d728..7f235a4541 100644 --- a/libs/permission/IAppOpsService.cpp +++ b/libs/permission/IAppOpsService.cpp @@ -26,8 +26,6 @@ namespace android { -using android::content::AttributionSourceState; - // ---------------------------------------------------------------------- class BpAppOpsService : public BpInterface<IAppOpsService> @@ -38,30 +36,31 @@ public: { } - virtual int32_t checkOperationWithState(int32_t code, - const AttributionSourceState &attributionSourceState) { + virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); - data.writeParcelable(attributionSourceState); - remote()->transact(CHECK_OPERATION_WITH_STATE_TRANSACTION, data, &reply); + data.writeInt32(uid); + data.writeString16(packageName); + remote()->transact(CHECK_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; return reply.readInt32(); } - virtual int32_t noteOperationWithState(int32_t code, - const AttributionSourceState& attributionSourceState, - bool shouldCollectAsyncNotedOp, const String16& message, - bool shouldCollectMessage) { + virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, + const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, + const String16& message, bool shouldCollectMessage) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); - data.writeParcelable(attributionSourceState); + data.writeInt32(uid); + data.writeString16(packageName); + data.writeString16(attributionTag); data.writeBool(shouldCollectAsyncNotedOp); data.writeString16(message); data.writeBool(shouldCollectMessage); - remote()->transact(NOTE_OPERATION_WITH_STATE_TRANSACTION, data, &reply); + remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; // TODO b/184855056: extract to class @@ -70,20 +69,22 @@ public: return reply.readInt32(); } - virtual int32_t startOperationWithState(const sp<IBinder>& token, int32_t code, - const AttributionSourceState& attributionSourceState, bool startIfModeDefault, - bool shouldCollectAsyncNotedOp, const String16& message, + virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, + const String16& packageName, const std::optional<String16>& attributionTag, + bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message, bool shouldCollectMessage) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); - data.writeParcelable(attributionSourceState); + data.writeInt32(uid); + data.writeString16(packageName); + data.writeString16(attributionTag); data.writeBool(startIfModeDefault); data.writeBool(shouldCollectAsyncNotedOp); data.writeString16(message); data.writeBool(shouldCollectMessage); - remote()->transact(START_OPERATION_WITH_STATE_TRANSACTION, data, &reply); + remote()->transact(START_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; // TODO b/184855056: extract to class @@ -92,14 +93,16 @@ public: return reply.readInt32(); } - virtual void finishOperationWithState(const sp<IBinder>& token, int32_t code, - const AttributionSourceState& attributionSourceState) { + virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, + const String16& packageName, const std::optional<String16>& attributionTag) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); - data.writeParcelable(attributionSourceState); - remote()->transact(FINISH_OPERATION_WITH_STATE_TRANSACTION, data, &reply); + data.writeInt32(uid); + data.writeString16(packageName); + data.writeString16(attributionTag); + remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply); } virtual void startWatchingMode(int32_t op, const String16& packageName, @@ -186,65 +189,59 @@ status_t BnAppOpsService::onTransact( { //printf("AppOpsService received: "); data.print(); switch(code) { - case CHECK_OPERATION_WITH_STATE_TRANSACTION: { + case CHECK_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); int32_t code = data.readInt32(); - AttributionSourceState attributionSourceState; - status_t status = data.readParcelable(&attributionSourceState); - if (status != NO_ERROR) { - return status; - } - int32_t res = checkOperationWithState(code, attributionSourceState); + int32_t uid = data.readInt32(); + String16 packageName = data.readString16(); + int32_t res = checkOperation(code, uid, packageName); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; - case NOTE_OPERATION_WITH_STATE_TRANSACTION: { + case NOTE_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); int32_t code = data.readInt32(); - AttributionSourceState attributionSourceState; - status_t status = data.readParcelable(&attributionSourceState); - if (status != NO_ERROR) { - return status; - } + int32_t uid = data.readInt32(); + String16 packageName = data.readString16(); + std::optional<String16> attributionTag; + data.readString16(&attributionTag); bool shouldCollectAsyncNotedOp = data.readBool(); String16 message = data.readString16(); bool shouldCollectMessage = data.readBool(); - int32_t res = noteOperationWithState(code, attributionSourceState, + int32_t res = noteOperation(code, uid, packageName, attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; - case START_OPERATION_WITH_STATE_TRANSACTION: { + case START_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); sp<IBinder> token = data.readStrongBinder(); int32_t code = data.readInt32(); - AttributionSourceState attributionSourceState; - status_t status = data.readParcelable(&attributionSourceState); - if (status != NO_ERROR) { - return status; - } + int32_t uid = data.readInt32(); + String16 packageName = data.readString16(); + std::optional<String16> attributionTag; + data.readString16(&attributionTag); bool startIfModeDefault = data.readBool(); bool shouldCollectAsyncNotedOp = data.readBool(); String16 message = data.readString16(); bool shouldCollectMessage = data.readBool(); - int32_t res = startOperationWithState(token, code, attributionSourceState, + int32_t res = startOperation(token, code, uid, packageName, attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; - case FINISH_OPERATION_WITH_STATE_TRANSACTION: { + case FINISH_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); sp<IBinder> token = data.readStrongBinder(); int32_t code = data.readInt32(); - AttributionSourceState attributionSourceState; - status_t status = data.readParcelable(&attributionSourceState); - if (status != NO_ERROR) { - return status; - } - finishOperationWithState(token, code, attributionSourceState); + int32_t uid = data.readInt32(); + String16 packageName = data.readString16(); + std::optional<String16> attributionTag; + data.readString16(&attributionTag); + finishOperation(token, code, uid, packageName, attributionTag); reply->writeNoException(); return NO_ERROR; } break; diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h index a5fdc54f28..918fcdbce1 100644 --- a/libs/permission/include/binder/IAppOpsService.h +++ b/libs/permission/include/binder/IAppOpsService.h @@ -16,7 +16,6 @@ #pragma once -#include <android/content/AttributionSourceState.h> #include <binder/IAppOpsCallback.h> #include <binder/IInterface.h> @@ -28,24 +27,23 @@ namespace android { -using android::content::AttributionSourceState; - // ---------------------------------------------------------------------- class IAppOpsService : public IInterface { public: DECLARE_META_INTERFACE(AppOpsService) - virtual int32_t checkOperationWithState(int32_t code, - const AttributionSourceState& attributionSourceState) = 0; - virtual int32_t noteOperationWithState(int32_t code, - const AttributionSourceState& attributionSourceState, bool shouldCollectAsyncNotedOp, + + virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0; + virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, + const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, const String16& message, bool shouldCollectMessage) = 0; - virtual int32_t startOperationWithState(const sp<IBinder>& token, int32_t code, - const AttributionSourceState& attributionSourceState, bool startIfModeDefault, - bool shouldCollectAsyncNotedOp, const String16& message, bool shouldCollectMessage) = 0; - virtual void finishOperationWithState(const sp<IBinder>& token, int32_t code, - const AttributionSourceState& attributionSourceState) = 0; + virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, + const String16& packageName, const std::optional<String16>& attributionTag, + bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message, + bool shouldCollectMessage) = 0; + virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, + const String16& packageName, const std::optional<String16>& attributionTag) = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0; virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0; @@ -58,10 +56,10 @@ public: int32_t flags, const sp<IAppOpsCallback>& callback) = 0; enum { - CHECK_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+50, - NOTE_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+52, - START_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+53, - FINISH_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+54, + CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1, + START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2, + FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3, START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4, STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5, PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6, diff --git a/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/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_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/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/inputflinger/Android.bp b/services/inputflinger/Android.bp index 10f386e0fd..45c9b5cf0b 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -244,6 +244,9 @@ phony { "Bug-115739809", "StructLayout_test", + // jni + "libservices.core", + // rust targets "libinput_rust_test", @@ -257,6 +260,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 1092bdb90e..e529bddea5 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -31,16 +31,30 @@ bool isFromMouse(const NotifyMotionArgs& args) { args.pointerProperties[0].toolType == ToolType::MOUSE; } +bool isHoverAction(int32_t action) { + return action == AMOTION_EVENT_ACTION_HOVER_ENTER || + action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT; +} + +bool isStylusHoverEvent(const NotifyMotionArgs& args) { + return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action); +} } // namespace // --- PointerChoreographer --- PointerChoreographer::PointerChoreographer(InputListenerInterface& listener, PointerChoreographerPolicyInterface& policy) - : mNextListener(listener), + : mTouchControllerConstructor([this]() REQUIRES(mLock) { + return mPolicy.createPointerController( + PointerControllerInterface::ControllerType::TOUCH); + }), + mNextListener(listener), mPolicy(policy), mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT), - mNotifiedPointerDisplayId(ADISPLAY_ID_NONE) {} + mNotifiedPointerDisplayId(ADISPLAY_ID_NONE), + mShowTouchesEnabled(false), + mStylusPointerIconEnabled(false) {} void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) { std::scoped_lock _l(mLock); @@ -69,8 +83,10 @@ NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& arg if (isFromMouse(args)) { return processMouseEventLocked(args); + } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) { + processStylusHoverEventLocked(args); } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) { - return processTouchscreenEventLocked(args); + processTouchscreenAndStylusEventLocked(args); } return args; } @@ -114,12 +130,70 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio * mouse device keeps moving and unfades the cursor. * For touch events, we do not need to populate the cursor position. */ -NotifyMotionArgs PointerChoreographer::processTouchscreenEventLocked(const NotifyMotionArgs& args) { +void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) { + if (args.displayId == ADISPLAY_ID_NONE) { + return; + } + if (const auto it = mMousePointersByDisplay.find(args.displayId); it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) { it->second->fade(PointerControllerInterface::Transition::GRADUAL); } - return args; + + if (!mShowTouchesEnabled) { + return; + } + + // Get the touch pointer controller for the device, or create one if it doesn't exist. + auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor); + + PointerControllerInterface& pc = *it->second; + + const PointerCoords* coords = args.pointerCoords.data(); + const int32_t maskedAction = MotionEvent::getActionMasked(args.action); + const uint8_t actionIndex = MotionEvent::getActionIndex(args.action); + std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex; + BitSet32 idBits; + if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) { + for (size_t i = 0; i < args.getPointerCount(); i++) { + if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) { + continue; + } + uint32_t id = args.pointerProperties[i].id; + idToIndex[id] = i; + idBits.markBit(id); + } + } + // The PointerController already handles setting spots per-display, so + // we do not need to manually manage display changes for touch spots for now. + pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId); +} + +void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) { + if (args.displayId == ADISPLAY_ID_NONE) { + return; + } + + if (args.getPointerCount() != 1) { + LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: " + << args.dump(); + } + + // Get the stylus pointer controller for the device, or create one if it doesn't exist. + auto [it, _] = + mStylusPointersByDevice.try_emplace(args.deviceId, + getStylusControllerConstructor(args.displayId)); + + PointerControllerInterface& pc = *it->second; + + const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X); + const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); + pc.setPosition(x, y); + if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) { + pc.fade(PointerControllerInterface::Transition::IMMEDIATE); + } else { + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + } } void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) { @@ -135,9 +209,17 @@ void PointerChoreographer::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) { @@ -153,12 +235,25 @@ 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"; } @@ -175,8 +270,16 @@ int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisp return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId; } +InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) { + auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(), + [deviceId](const auto& info) { return info.getId() == deviceId; }); + return it != mInputDeviceInfos.end() ? &(*it) : nullptr; +} + void PointerChoreographer::updatePointerControllersLocked() { std::set<int32_t /*displayId*/> mouseDisplaysToKeep; + std::set<DeviceId> touchDevicesToKeep; + std::set<DeviceId> stylusDevicesToKeep; // Mark the displayIds or deviceIds of PointerControllers currently needed. for (const auto& info : mInputDeviceInfos) { @@ -187,17 +290,25 @@ void PointerChoreographer::updatePointerControllersLocked() { getTargetMouseDisplayLocked(info.getAssociatedDisplayId()); mouseDisplaysToKeep.insert(resolvedDisplayId); } + if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled && + info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) { + touchDevicesToKeep.insert(info.getId()); + } + if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled && + info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) { + stylusDevicesToKeep.insert(info.getId()); + } } // Remove PointerControllers no longer needed. - // This has the side-effect of fading pointers or clearing spots before removal. std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) { - auto& [displayId, controller] = pair; - if (mouseDisplaysToKeep.find(displayId) == mouseDisplaysToKeep.end()) { - controller->fade(PointerControllerInterface::Transition::IMMEDIATE); - return true; - } - return false; + return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end(); + }); + std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) { + return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end(); + }); + std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) { + return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end(); }); // Notify the policy if there's a change on the pointer display ID. @@ -234,10 +345,17 @@ void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) { void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) { std::scoped_lock _l(mLock); for (const auto& viewport : viewports) { - if (const auto it = mMousePointersByDisplay.find(viewport.displayId); + const int32_t displayId = viewport.displayId; + if (const auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) { it->second->setDisplayViewport(viewport); } + for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) { + const InputDeviceInfo* info = findInputDeviceLocked(deviceId); + if (info && info->getAssociatedDisplayId() == displayId) { + stylusPointerController->setDisplayViewport(viewport); + } + } } mViewports = viewports; notifyPointerDisplayIdChangedLocked(); @@ -263,6 +381,24 @@ FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) { return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION}; } +void PointerChoreographer::setShowTouchesEnabled(bool enabled) { + std::scoped_lock _l(mLock); + if (mShowTouchesEnabled == enabled) { + return; + } + mShowTouchesEnabled = enabled; + updatePointerControllersLocked(); +} + +void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) { + std::scoped_lock _l(mLock); + if (mStylusPointerIconEnabled == enabled) { + return; + } + mStylusPointerIconEnabled = enabled; + updatePointerControllersLocked(); +} + PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor( int32_t displayId) { std::function<std::shared_ptr<PointerControllerInterface>()> ctor = @@ -277,4 +413,18 @@ PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseContro 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 c1b900f8a5..26d2fef5c3 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -56,6 +56,8 @@ public: virtual std::optional<DisplayViewport> getViewportForPointerDevice( int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0; virtual FloatPoint getMouseCursorPosition(int32_t displayId) = 0; + virtual void setShowTouchesEnabled(bool enabled) = 0; + virtual void setStylusPointerIconEnabled(bool enabled) = 0; /** * This method may be called on any thread (usually by the input manager on a binder thread). */ @@ -73,6 +75,8 @@ public: std::optional<DisplayViewport> getViewportForPointerDevice( int32_t associatedDisplayId) override; FloatPoint getMouseCursorPosition(int32_t displayId) override; + void setShowTouchesEnabled(bool enabled) override; + void setStylusPointerIconEnabled(bool enabled) override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; @@ -91,14 +95,19 @@ private: void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock); const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock); int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock); + InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock); NotifyMotionArgs processMotion(const NotifyMotionArgs& args); NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); - NotifyMotionArgs processTouchscreenEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); + void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); + void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); + void processDeviceReset(const NotifyDeviceResetArgs& args); using ControllerConstructor = ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>; + ControllerConstructor mTouchControllerConstructor GUARDED_BY(mLock); ControllerConstructor getMouseControllerConstructor(int32_t displayId) REQUIRES(mLock); + ControllerConstructor getStylusControllerConstructor(int32_t displayId) REQUIRES(mLock); std::mutex mLock; @@ -107,11 +116,17 @@ private: 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/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/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index c8528e1a03..23ac088f7f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4547,7 +4547,6 @@ void InputDispatcher::notifySwitch(const NotifySwitchArgs& args) { } void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) { - // TODO(b/308677868) Remove device reset from the InputListener interface if (debugInboundEventDetails()) { ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args.eventTime, args.deviceId); @@ -4693,30 +4692,6 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev } mLock.lock(); - - if (policyFlags & POLICY_FLAG_FILTERED) { - // The events from InputFilter impersonate real hardware devices. Check these - // events for consistency and print an error. An inconsistent event sent from - // InputFilter could cause a crash in the later stages of dispatching pipeline. - auto [it, _] = - mInputFilterVerifiersByDisplay - .try_emplace(displayId, - StringPrintf("Injection on %" PRId32, displayId)); - InputVerifier& verifier = it->second; - - Result<void> result = - verifier.processMovement(resolvedDeviceId, motionEvent.getSource(), - motionEvent.getAction(), - motionEvent.getPointerCount(), - motionEvent.getPointerProperties(), - motionEvent.getSamplePointerCoords(), flags); - if (!result.ok()) { - logDispatchStateLocked(); - LOG(ERROR) << "Inconsistent event: " << motionEvent - << ", reason: " << result.error(); - } - } - const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes(); const size_t pointerCount = motionEvent.getPointerCount(); const std::vector<PointerProperties> @@ -6760,7 +6735,6 @@ void InputDispatcher::displayRemoved(int32_t displayId) { // Remove the associated touch mode state. mTouchModePerDisplay.erase(displayId); mVerifiersByDisplay.erase(displayId); - mInputFilterVerifiersByDisplay.erase(displayId); } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -6953,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 f0f67721e7..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 { @@ -286,8 +287,7 @@ private: void transformMotionEntryForInjectionLocked(MotionEntry&, const ui::Transform& injectedTransform) const REQUIRES(mLock); - // Per-display correction of injected events - std::map</*displayId*/ int32_t, InputVerifier> mInputFilterVerifiersByDisplay GUARDED_BY(mLock); + std::condition_variable mInjectionSyncFinished; void incrementPendingForegroundDispatches(EventEntry& entry); void decrementPendingForegroundDispatches(EventEntry& entry); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 16cc266938..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; } 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/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 8837b25378..ef74a55860 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -48,8 +48,8 @@ struct FloatPoint { */ class PointerControllerInterface { protected: - PointerControllerInterface() { } - virtual ~PointerControllerInterface() { } + PointerControllerInterface() {} + virtual ~PointerControllerInterface() {} public: /** @@ -63,6 +63,10 @@ public: LEGACY, // Represents a single mouse pointer. MOUSE, + // Represents multiple touch spots. + TOUCH, + // Represents a single stylus pointer. + STYLUS, }; /* Dumps the state of the pointer controller. */ @@ -121,7 +125,7 @@ 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/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 9c87c62a7c..2dd05f5c66 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -131,7 +131,7 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { bumpGeneration(); } } - if (shouldSimulateStylusWithTouch() && outPointer.toolType == ToolType::FINGER) { + if (mShouldSimulateStylusWithTouch && outPointer.toolType == ToolType::FINGER) { outPointer.toolType = ToolType::STYLUS; } @@ -177,6 +177,18 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { mMultiTouchMotionAccumulator.finishSync(); } +std::list<NotifyArgs> MultiTouchInputMapper::reconfigure(nsecs_t when, + const InputReaderConfiguration& config, + ConfigurationChanges changes) { + const bool simulateStylusWithTouch = + sysprop::InputProperties::simulate_stylus_with_touch().value_or(false); + if (simulateStylusWithTouch != mShouldSimulateStylusWithTouch) { + mShouldSimulateStylusWithTouch = simulateStylusWithTouch; + bumpGeneration(); + } + return TouchInputMapper::reconfigure(when, config, changes); +} + void MultiTouchInputMapper::configureRawPointerAxes() { TouchInputMapper::configureRawPointerAxes(); @@ -211,14 +223,7 @@ void MultiTouchInputMapper::configureRawPointerAxes() { bool MultiTouchInputMapper::hasStylus() const { return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() || - shouldSimulateStylusWithTouch(); -} - -bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const { - static const bool SIMULATE_STYLUS_WITH_TOUCH = - sysprop::InputProperties::simulate_stylus_with_touch().value_or(false); - return SIMULATE_STYLUS_WITH_TOUCH && - mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN; + mShouldSimulateStylusWithTouch; } } // namespace android diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index 1d788dffd4..5c173f365e 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -32,6 +32,9 @@ public: [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override; [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override; + [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when, + const InputReaderConfiguration& config, + ConfigurationChanges changes) override; protected: void syncTouch(nsecs_t when, RawState* outState) override; @@ -41,13 +44,6 @@ protected: private: explicit MultiTouchInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig); - // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this - // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device. - // It is used to simulate stylus events for debugging and testing on a device that does not - // support styluses. It can be enabled using - // "adb shell setprop persist.debug.input.simulate_stylus_with_touch true", - // and requires a reboot to take effect. - inline bool shouldSimulateStylusWithTouch() const; // If the slot is in use, return the bit id. Return std::nullopt otherwise. std::optional<int32_t> getActiveBitId(const MultiTouchMotionAccumulator::Slot& inSlot); @@ -58,6 +54,15 @@ private: int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; bool mStylusMtToolSeen{false}; + + // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this + // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device. + // It is used to simulate stylus events for debugging and testing on a device that does not + // support styluses. It can be enabled using + // "adb shell setprop debug.input.simulate_stylus_with_touch true". + // After enabling, the touchscreen will need to be reconfigured. A reconfiguration usually + // happens when turning the screen on/off or by rotating the device orientation. + bool mShouldSimulateStylusWithTouch{false}; }; } // namespace android 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/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 c75f6ed7e5..d7e40b3cc1 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -37,6 +37,7 @@ public: void setDisplayViewport(const DisplayViewport& viewport) override; void assertPosition(float x, float y); + void assertSpotCount(int32_t displayId, int32_t count); bool isPointerShown(); private: diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h new file mode 100644 index 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 3c2b31d770..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> @@ -851,29 +852,10 @@ 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, bool handled = false) { InputEvent* event; @@ -897,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; @@ -928,12 +910,12 @@ public: * To be used together with "receiveEvent" to complete the consumption of an event. */ void finishEvent(uint32_t consumeSeq, bool handled = true) { - const status_t status = mConsumer->sendFinishedSignal(consumeSeq, handled); + 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); } @@ -1091,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; @@ -1436,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))); @@ -1480,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( @@ -2614,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); @@ -2645,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); @@ -2704,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(); } /** @@ -3029,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 = @@ -3073,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))); @@ -3309,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); @@ -3335,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, @@ -3348,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, @@ -3358,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, @@ -3367,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(); } @@ -6773,8 +6748,8 @@ TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) { 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; @@ -8156,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/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index da2e205ca5..68f58571f5 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -27,6 +27,7 @@ namespace android { using ControllerType = PointerControllerInterface::ControllerType; +using testing::AllOf; namespace { @@ -37,12 +38,18 @@ 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) { @@ -60,6 +67,8 @@ static std::vector<DisplayViewport> createViewports(std::vector<int32_t> display for (auto displayId : displayIds) { DisplayViewport viewport; viewport.displayId = displayId; + viewport.logicalRight = DISPLAY_WIDTH; + viewport.logicalBottom = DISPLAY_HEIGHT; viewports.push_back(viewport); } return viewports; @@ -115,6 +124,7 @@ private: 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; } @@ -385,4 +395,659 @@ TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointe 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/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/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/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/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 4fa5f19da9..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)); diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 81bfe9edfe..2ae80de42a 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -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; diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 2f891b3ce8..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); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index a44e4be1ce..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)); 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 1d9f9ce73a..6be57d480d 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -498,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(); @@ -545,10 +545,10 @@ status_t HWComposer::getDeviceCompositionChanges( } if (canSkipValidate) { - sp<Fence> outPresentFence; + sp<Fence> outPresentFence = Fence::NO_FENCE; uint32_t state = UINT32_MAX; - error = hwcDisplay->presentOrValidate(expectedPresentTime, &numTypes, &numRequests, - &outPresentFence, &state); + error = hwcDisplay->presentOrValidate(expectedPresentTime, frameInterval.getPeriodNsecs(), + &numTypes, &numRequests, &outPresentFence, &state); if (!hasChangesError(error)) { RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR); } @@ -563,7 +563,8 @@ 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)) { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 51e93197a8..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; @@ -347,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, 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/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 66ea15ca2d..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); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index f71591044d..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; diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 6752a0bafa..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" 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 8fc9cba794..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 { @@ -167,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 8d18769e5a..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()) diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 03ab0df3cd..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}; 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 56a4ae2569..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" @@ -654,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) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index a02180af8d..c78051a278 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -234,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); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 3e7ec492fa..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" diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 57aa010740..f5f93ce2f1 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -29,13 +29,13 @@ #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> #include <gui/TraceUtils.h> #include <utils/Log.h> -#include "FlagManager.h" #include "RefreshRateSelector.h" #include "VSyncPredictor.h" 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 4d02b44201..644b6ef30e 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; @@ -2250,6 +2251,10 @@ void SurfaceFlinger::updateLayerHistory(nsecs_t now) { .isFrontBuffered = snapshot->isFrontBuffered(), }; + if (snapshot->changes.any(Changes::Geometry | Changes::Visibility)) { + mScheduler->setLayerProperties(snapshot->sequence, layerProps); + } + if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) { mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence, snapshot->defaultFrameRateCompatibility); @@ -2621,6 +2626,10 @@ 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); @@ -2913,7 +2922,6 @@ 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; @@ -3801,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()); @@ -6762,8 +6769,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1007: // Unused. return NAME_NOT_FOUND; case 1008: // Toggle forced GPU composition. - mDebugDisableHWC = data.readInt32() != 0; - scheduleRepaint(); + sfdo_forceClientComposition(data.readInt32() != 0); return NO_ERROR; case 1009: // Toggle use of transform hint. mDebugDisableTransformHint = data.readInt32() != 0; @@ -7573,7 +7579,7 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, args.allowProtected, args.grayscale, captureListener); } -void SurfaceFlinger::captureDisplay(DisplayId displayId, +void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { ui::LayerStack layerStack; wp<const DisplayDevice> displayWeak; @@ -7592,10 +7598,23 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, size = display->getLayerStackSpaceRect().getSize(); } + size.width *= args.frameScaleX; + size.height *= args.frameScaleY; + + // We could query a real value for this but it'll be a long, long time until we support + // displays that need upwards of 1GB per buffer so... + constexpr auto kMaxTextureSize = 16384; + if (size.width <= 0 || size.height <= 0 || size.width >= kMaxTextureSize || + size.height >= kMaxTextureSize) { + ALOGE("capture display resolved to invalid size %d x %d", size.width, size.height); + invokeScreenCaptureError(BAD_VALUE, captureListener); + return; + } + RenderAreaFuture renderAreaFuture = ftl::defer([=] { - return DisplayRenderArea::create(displayWeak, Rect(), size, ui::Dataspace::UNKNOWN, + return DisplayRenderArea::create(displayWeak, Rect(), size, args.dataspace, false /* useIdentityTransform */, - false /* hintForSeamlessTransition */, + args.hintForSeamlessTransition, false /* captureSecureLayers */); }); @@ -7619,8 +7638,8 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, constexpr bool kAllowProtected = false; constexpr bool kGrayscale = false; - captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, - ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale, captureListener); + captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, args.pixelFormat, + kAllowProtected, kGrayscale, captureListener); } void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, @@ -8413,7 +8432,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) { @@ -9051,6 +9071,11 @@ void SurfaceFlinger::sfdo_scheduleCommit() { setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded | eTraversalNeeded); } +void SurfaceFlinger::sfdo_forceClientComposition(bool enabled) { + mDebugDisableHWC = enabled; + scheduleRepaint(); +} + // gui::ISurfaceComposer binder::Status SurfaceComposerAIDL::bootFinished() { @@ -9413,13 +9438,14 @@ binder::Status SurfaceComposerAIDL::captureDisplay( } binder::Status SurfaceComposerAIDL::captureDisplayById( - int64_t displayId, const sp<IScreenCaptureListener>& captureListener) { + int64_t displayId, const CaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) { // status_t status; IPCThreadState* ipc = IPCThreadState::self(); const int uid = ipc->getCallingUid(); if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) { std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId)); - mFlinger->captureDisplay(*id, captureListener); + mFlinger->captureDisplay(*id, args, captureListener); } else { invokeScreenCaptureError(PERMISSION_DENIED, captureListener); } @@ -9769,6 +9795,11 @@ binder::Status SurfaceComposerAIDL::scheduleCommit() { return binder::Status::ok(); } +binder::Status SurfaceComposerAIDL::forceClientComposition(bool enabled) { + mFlinger->sfdo_forceClientComposition(enabled); + return binder::Status::ok(); +} + binder::Status SurfaceComposerAIDL::updateSmallAreaDetection(const std::vector<int32_t>& appIds, const std::vector<float>& thresholds) { status_t status; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 1e90340449..9e6da3f80b 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" @@ -531,7 +531,7 @@ private: const sp<IBinder>& layerHandle = nullptr); void captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); - void captureDisplay(DisplayId, const sp<IScreenCaptureListener>&); + void captureDisplay(DisplayId, const CaptureArgs&, const sp<IScreenCaptureListener>&); void captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats); @@ -1462,6 +1462,7 @@ private: void sfdo_setDebugFlash(int delay); void sfdo_scheduleComposite(); void sfdo_scheduleCommit(); + void sfdo_forceClientComposition(bool enabled); }; class SurfaceComposerAIDL : public gui::BnSurfaceComposer { @@ -1507,7 +1508,8 @@ public: binder::Status setGameContentType(const sp<IBinder>& display, bool on) override; binder::Status captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override; - binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override; + binder::Status captureDisplayById(int64_t, const CaptureArgs&, + const sp<IScreenCaptureListener>&) override; binder::Status captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override; @@ -1572,6 +1574,7 @@ public: binder::Status setDebugFlash(int delay) override; binder::Status scheduleComposite() override; binder::Status scheduleCommit() override; + binder::Status forceClientComposition(bool enabled) override; binder::Status updateSmallAreaDetection(const std::vector<int32_t>& appIds, const std::vector<float>& thresholds) override; binder::Status setSmallAreaDetectionThreshold(int32_t appId, float threshold) override; 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 6d0dbc7978..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> @@ -110,6 +110,7 @@ void FlagManager::dump(std::string& result) const { /// Trunk stable server flags /// DUMP_SERVER_FLAG(late_boot_misc2); DUMP_SERVER_FLAG(dont_skip_on_early); + DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display); /// Trunk stable readonly flags /// DUMP_READ_ONLY_FLAG(connected_display); @@ -190,6 +191,7 @@ FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_prese /// 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 cefce9bcda..e3e4f80905 100644 --- a/services/surfaceflinger/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -49,6 +49,7 @@ public: /// Trunk stable server flags /// bool late_boot_misc2() const; bool dont_skip_on_early() const; + bool refresh_rate_overlay_on_external_display() const; /// Trunk stable readonly flags /// bool connected_display() const; diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp index 243b8e04df..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", 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/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 a81f9b80de..bb3c94a4eb 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -70,3 +70,9 @@ flag { 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/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/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 58d7a40fc8..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; @@ -217,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 diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 190c0e8c10..befef482b9 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -839,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/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 57babafac3..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) { 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/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/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index d0b2199c58..bca14f5217 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -623,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); 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/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index a87f82fcbd..436e6c6b7c 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -109,6 +109,7 @@ cc_library_shared { "libnativeloader_lazy", "libnativewindow", "libvndksupport", + "libdl_android", "android.hardware.graphics.common@1.0", "libSurfaceFlingerProp", ], diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 5d7a4aa170..0e45d2d8c4 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -46,6 +46,8 @@ using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; +extern "C" android_namespace_t* android_get_exported_namespace(const char*); + // #define ENABLE_ALLOC_CALLSTACKS 1 #if ENABLE_ALLOC_CALLSTACKS #include <utils/CallStack.h> @@ -159,6 +161,7 @@ const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{ "ro.board.platform", }}; constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW; +constexpr char RO_VULKAN_APEX_PROPERTY[] = "ro.vulkan.apex"; // LoadDriver returns: // * 0 when succeed, or @@ -166,6 +169,7 @@ constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW; // * -EINVAL when fail to find HAL_MODULE_INFO_SYM_AS_STR or // HWVULKAN_HARDWARE_MODULE_ID in the library. int LoadDriver(android_namespace_t* library_namespace, + const char* ns_name, const hwvulkan_module_t** module) { ATRACE_CALL(); @@ -183,8 +187,10 @@ 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 %s namespace: %s.", + lib_name.c_str(), ns_name, dlerror()); + } } else { // load built-in driver so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS); @@ -211,12 +217,30 @@ int LoadDriver(android_namespace_t* library_namespace, return 0; } +int LoadDriverFromApex(const hwvulkan_module_t** module) { + ATRACE_CALL(); + + auto apex_name = android::base::GetProperty(RO_VULKAN_APEX_PROPERTY, ""); + if (apex_name == "") { + return -ENOENT; + } + // Get linker namespace for Vulkan APEX + std::replace(apex_name.begin(), apex_name.end(), '.', '_'); + auto ns = android_get_exported_namespace(apex_name.c_str()); + if (!ns) { + return -ENOENT; + } + android::GraphicsEnv::getInstance().setDriverToLoad( + android::GpuStatsInfo::Driver::VULKAN); + return LoadDriver(ns, apex_name.c_str(), module); +} + int LoadBuiltinDriver(const hwvulkan_module_t** module) { ATRACE_CALL(); android::GraphicsEnv::getInstance().setDriverToLoad( android::GpuStatsInfo::Driver::VULKAN); - return LoadDriver(nullptr, module); + return LoadDriver(nullptr, nullptr, module); } int LoadUpdatedDriver(const hwvulkan_module_t** module) { @@ -227,7 +251,7 @@ int LoadUpdatedDriver(const hwvulkan_module_t** module) { return -ENOENT; android::GraphicsEnv::getInstance().setDriverToLoad( android::GpuStatsInfo::Driver::VULKAN_UPDATED); - int result = LoadDriver(ns, module); + int result = LoadDriver(ns, "updatable gfx driver", module); if (result != 0) { LOG_ALWAYS_FATAL( "couldn't find an updated Vulkan implementation from %s", @@ -256,6 +280,9 @@ bool Hal::Open() { result = LoadUpdatedDriver(&module); if (result == -ENOENT) { + result = LoadDriverFromApex(&module); + } + if (result == -ENOENT) { result = LoadBuiltinDriver(&module); } if (result != 0) { |