Merge changes I17b91fb6,Ibc671cec into main
* changes:
EGL Multifile Blobcache: Add status file
EGL Multifile BlobCache: Limit entry count
diff --git a/Android.bp b/Android.bp
index 7f1ef67..8c4dfbb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -97,6 +97,12 @@
],
}
+aidl_library {
+ name: "PersistableBundle_aidl",
+ hdrs: ["aidl/binder/android/os/PersistableBundle.aidl"],
+ strip_import_prefix: "aidl/binder",
+}
+
cc_library_headers {
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl
index 493ecb4..248e973 100644
--- a/aidl/binder/android/os/PersistableBundle.aidl
+++ b/aidl/binder/android/os/PersistableBundle.aidl
@@ -17,4 +17,4 @@
package android.os;
-@JavaOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h" ndk_header "android/persistable_bundle_aidl.h";
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 860a2d8..23f185e 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -104,6 +104,8 @@
"libvintf",
"libbinderdebug",
"packagemanager_aidl-cpp",
+ "server_configurable_flags",
+ "device_policy_aconfig_flags_c_lib",
],
srcs: [
"DumpstateService.cpp",
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index c787113..ba0a38a 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -37,6 +37,8 @@
Dumpstate* ds = nullptr;
int32_t calling_uid = -1;
std::string calling_package;
+ int32_t user_id = -1;
+ bool keep_bugreport_on_retrieval = false;
};
static binder::Status exception(uint32_t code, const std::string& msg,
@@ -60,7 +62,7 @@
[[noreturn]] static void* dumpstate_thread_retrieve(void* data) {
std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
- ds_info->ds->Retrieve(ds_info->calling_uid, ds_info->calling_package);
+ ds_info->ds->Retrieve(ds_info->calling_uid, ds_info->calling_package, ds_info->keep_bugreport_on_retrieval);
MYLOGD("Finished retrieving a bugreport. Exiting.\n");
exit(0);
}
@@ -201,9 +203,10 @@
}
binder::Status DumpstateService::retrieveBugreport(
- int32_t calling_uid, const std::string& calling_package,
+ int32_t calling_uid, const std::string& calling_package, int32_t user_id,
android::base::unique_fd bugreport_fd,
const std::string& bugreport_file,
+ const bool keep_bugreport_on_retrieval,
const sp<IDumpstateListener>& listener) {
ds_ = &(Dumpstate::GetInstance());
@@ -211,6 +214,8 @@
ds_info->ds = ds_;
ds_info->calling_uid = calling_uid;
ds_info->calling_package = calling_package;
+ ds_info->user_id = user_id;
+ ds_info->keep_bugreport_on_retrieval = keep_bugreport_on_retrieval;
ds_->listener_ = listener;
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
// Use a /dev/null FD when initializing options since none is provided.
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index dd73319..7b76c36 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -48,8 +48,10 @@
binder::Status retrieveBugreport(int32_t calling_uid,
const std::string& calling_package,
+ int32_t user_id,
android::base::unique_fd bugreport_fd,
const std::string& bugreport_file,
+ const bool keep_bugreport_on_retrieval,
const sp<IDumpstateListener>& listener)
override;
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index fa9bcf3..97c470e 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -58,6 +58,9 @@
// Defer user consent.
const int BUGREPORT_FLAG_DEFER_CONSENT = 0x2;
+ // Keep bugreport stored after retrieval.
+ const int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = 0x4;
+
/**
* Speculatively pre-dumps UI data for a bugreport request that might come later.
*
@@ -116,12 +119,16 @@
*
* @param callingUid UID of the original application that requested the report.
* @param callingPackage package of the original application that requested the report.
+ * @param userId user Id of the original package that requested the report.
* @param bugreportFd the file to which the zipped bugreport should be written
* @param bugreportFile the path of the bugreport file
+ * @param keepBugreportOnRetrieval boolean to indicate if the bugreport should be kept in the
+ * platform after it has been retrieved by the caller.
* @param listener callback for updates; optional
*/
- void retrieveBugreport(int callingUid, @utf8InCpp String callingPackage,
+ void retrieveBugreport(int callingUid, @utf8InCpp String callingPackage, int userId,
FileDescriptor bugreportFd,
@utf8InCpp String bugreportFile,
+ boolean keepBugreportOnRetrieval,
IDumpstateListener listener);
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 33f6d38..17e5cc1 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -59,6 +59,7 @@
#include <vector>
#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
+#include <android_app_admin_flags.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
@@ -1088,8 +1089,14 @@
// This function copies into the .zip the system trace that was snapshotted
// by the early call to MaybeSnapshotSystemTrace(), if any background
// tracing was happening.
- if (!ds.has_system_trace_) {
- // No background trace was happening at the time dumpstate was invoked.
+ bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
+ if (!system_trace_exists) {
+ // No background trace was happening at the time MaybeSnapshotSystemTrace() was invoked.
+ if (!PropertiesHelper::IsUserBuild()) {
+ MYLOGI(
+ "No system traces found. Check for previously uploaded traces by looking for "
+ "go/trace-uuid in logcat")
+ }
return;
}
ds.AddZipEntry(
@@ -1649,8 +1656,6 @@
dump_board = ds.dump_pool_->enqueueTaskWithFd(
DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
- post_process_ui_traces = ds.dump_pool_->enqueueTask(
- POST_PROCESS_UI_TRACES_TASK, &Dumpstate::MaybePostProcessUiTraces, &ds);
}
// Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1860,12 +1865,6 @@
DumpIncidentReport);
}
- if (ds.dump_pool_) {
- WaitForTask(std::move(post_process_ui_traces));
- } else {
- RUN_SLOW_FUNCTION_AND_LOG(POST_PROCESS_UI_TRACES_TASK, MaybePostProcessUiTraces);
- }
-
MaybeAddUiTracesToZip();
return Dumpstate::RunStatus::OK;
@@ -2977,14 +2976,17 @@
return status;
}
-Dumpstate::RunStatus Dumpstate::Retrieve(int32_t calling_uid, const std::string& calling_package) {
- Dumpstate::RunStatus status = RetrieveInternal(calling_uid, calling_package);
+Dumpstate::RunStatus Dumpstate::Retrieve(int32_t calling_uid, const std::string& calling_package,
+ const bool keep_bugreport_on_retrieval) {
+ Dumpstate::RunStatus status = RetrieveInternal(calling_uid, calling_package,
+ keep_bugreport_on_retrieval);
HandleRunStatus(status);
return status;
}
Dumpstate::RunStatus Dumpstate::RetrieveInternal(int32_t calling_uid,
- const std::string& calling_package) {
+ const std::string& calling_package,
+ const bool keep_bugreport_on_retrieval) {
consent_callback_ = new ConsentCallback();
const String16 incidentcompanion("incidentcompanion");
sp<android::IBinder> ics(
@@ -3019,9 +3021,12 @@
bool copy_succeeded =
android::os::CopyFileToFd(path_, options_->bugreport_fd.get());
- if (copy_succeeded) {
- android::os::UnlinkAndLogOnError(path_);
+
+ if (copy_succeeded && (!android::app::admin::flags::onboarding_bugreport_v2_enabled()
+ || !keep_bugreport_on_retrieval)) {
+ android::os::UnlinkAndLogOnError(path_);
}
+
return copy_succeeded ? Dumpstate::RunStatus::OK
: Dumpstate::RunStatus::ERROR;
}
@@ -3071,6 +3076,7 @@
}
void Dumpstate::PreDumpUiData() {
+ MaybeSnapshotSystemTrace();
MaybeSnapshotUiTraces();
}
@@ -3257,25 +3263,23 @@
// duration is logged into MYLOG instead.
PrintHeader();
- bool is_dumpstate_restricted = options_->telephony_only
- || options_->wifi_only
- || options_->limited_only;
- if (!is_dumpstate_restricted) {
- // Invoke critical dumpsys first to preserve system state, before doing anything else.
- RunDumpsysCritical();
- }
- MaybeTakeEarlyScreenshot();
-
+ bool is_dumpstate_restricted =
+ options_->telephony_only || options_->wifi_only || options_->limited_only;
if (!is_dumpstate_restricted) {
// Snapshot the system trace now (if running) to avoid that dumpstate's
// own activity pushes out interesting data from the trace ring buffer.
// The trace file is added to the zip by MaybeAddSystemTraceToZip().
MaybeSnapshotSystemTrace();
+ // Invoke critical dumpsys to preserve system state, before doing anything else.
+ RunDumpsysCritical();
+
// Snapshot the UI traces now (if running).
// The trace files will be added to bugreport later.
MaybeSnapshotUiTraces();
}
+
+ MaybeTakeEarlyScreenshot();
onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
if (options_->telephony_only) {
@@ -3372,6 +3376,19 @@
}
void Dumpstate::MaybeSnapshotSystemTrace() {
+ // When capturing traces via bugreport handler (BH), this function will be invoked twice:
+ // 1) When BH invokes IDumpstate::PreDumpUiData()
+ // 2) When BH invokes IDumpstate::startBugreport(flags = BUGREPORT_USE_PREDUMPED_UI_DATA)
+ // In this case we don't want to re-invoke perfetto in step 2.
+ // In all other standard invocation states, this function is invoked once
+ // without the flag BUGREPORT_USE_PREDUMPED_UI_DATA.
+ if (options_->use_predumped_ui_data) {
+ return;
+ }
+
+ // If a stale file exists already, remove it.
+ unlink(SYSTEM_TRACE_SNAPSHOT);
+
// If a background system trace is happening and is marked as "suitable for
// bugreport" (i.e. bugreport_score > 0 in the trace config), this command
// will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely)
@@ -3379,14 +3396,8 @@
// Note: this should not be enqueued as we need to freeze the trace before
// dumpstate starts. Otherwise the trace ring buffers will contain mostly
// the dumpstate's own activity which is irrelevant.
- int res = RunCommand(
- "SERIALIZE PERFETTO TRACE",
- {"perfetto", "--save-for-bugreport"},
- CommandOptions::WithTimeout(10)
- .DropRoot()
- .CloseAllFileDescriptorsOnExec()
- .Build());
- has_system_trace_ = res == 0;
+ RunCommand("SERIALIZE PERFETTO TRACE", {"perfetto", "--save-for-bugreport"},
+ CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build());
// MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
// file in the later stages.
}
@@ -3413,33 +3424,6 @@
"", command,
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
}
-
- // This command needs to be run as root
- static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector<std::string> {
- "service", "call", "SurfaceFlinger", "1042"
- };
- // Empty name because it's not intended to be classified as a bugreport section.
- // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
- RunCommand(
- "", SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES,
- CommandOptions::WithTimeout(10).Always().AsRoot().RedirectStderr().Build());
-}
-
-void Dumpstate::MaybePostProcessUiTraces() {
- if (PropertiesHelper::IsUserBuild()) {
- return;
- }
-
- RunCommand(
- // Empty name because it's not intended to be classified as a bugreport section.
- // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
- "", {
- "/system/xbin/su", "system",
- "/system/bin/layertracegenerator",
- "/data/misc/wmtrace/transactions_trace.winscope",
- "/data/misc/wmtrace/layers_trace_from_transactions.winscope"
- },
- CommandOptions::WithTimeout(120).Always().RedirectStderr().Build());
}
void Dumpstate::MaybeAddUiTracesToZip() {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0a032ec..c66fd1c 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -210,7 +210,9 @@
BUGREPORT_USE_PREDUMPED_UI_DATA =
android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA,
BUGREPORT_FLAG_DEFER_CONSENT =
- android::os::IDumpstate::BUGREPORT_FLAG_DEFER_CONSENT
+ android::os::IDumpstate::BUGREPORT_FLAG_DEFER_CONSENT,
+ BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL =
+ android::os::IDumpstate::BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL
};
static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
@@ -361,7 +363,8 @@
*
* Initialize() dumpstate before calling this method.
*/
- RunStatus Retrieve(int32_t calling_uid, const std::string& calling_package);
+ RunStatus Retrieve(int32_t calling_uid, const std::string& calling_package,
+ const bool keep_bugreport_on_retrieval);
@@ -473,11 +476,6 @@
// Whether it should take an screenshot earlier in the process.
bool do_early_screenshot_ = false;
- // This is set to true when the trace snapshot request in the early call to
- // MaybeSnapshotSystemTrace(). When this is true, the later stages of
- // dumpstate will append the trace to the zip archive.
- bool has_system_trace_ = false;
-
std::unique_ptr<Progress> progress_;
// When set, defines a socket file-descriptor use to report progress to bugreportz
@@ -562,7 +560,8 @@
private:
RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
- RunStatus RetrieveInternal(int32_t calling_uid, const std::string& calling_package);
+ RunStatus RetrieveInternal(int32_t calling_uid, const std::string& calling_package,
+ const bool keep_bugreport_on_retrieval);
RunStatus DumpstateDefaultAfterCritical();
RunStatus dumpstate();
@@ -570,7 +569,6 @@
void MaybeTakeEarlyScreenshot();
void MaybeSnapshotSystemTrace();
void MaybeSnapshotUiTraces();
- void MaybePostProcessUiTraces();
void MaybeAddUiTracesToZip();
void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index a417837..fc82886 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1000,7 +1000,6 @@
TEST_F(DumpstateTest, PreDumpUiData) {
// These traces are always enabled, i.e. they are always pre-dumped
const std::vector<std::filesystem::path> uiTraces = {
- std::filesystem::path{"/data/misc/wmtrace/transactions_trace.winscope"},
std::filesystem::path{"/data/misc/wmtrace/wm_transition_trace.winscope"},
std::filesystem::path{"/data/misc/wmtrace/shell_transition_trace.winscope"},
};
diff --git a/cmds/evemu-record/Android.bp b/cmds/evemu-record/Android.bp
new file mode 100644
index 0000000..1edacec
--- /dev/null
+++ b/cmds/evemu-record/Android.bp
@@ -0,0 +1,13 @@
+package {
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+rust_binary {
+ name: "evemu-record",
+ srcs: ["main.rs"],
+ rustlibs: [
+ "libclap",
+ "liblibc",
+ "libnix",
+ ],
+}
diff --git a/cmds/evemu-record/OWNERS b/cmds/evemu-record/OWNERS
new file mode 100644
index 0000000..c88bfe9
--- /dev/null
+++ b/cmds/evemu-record/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/cmds/evemu-record/evdev.rs b/cmds/evemu-record/evdev.rs
new file mode 100644
index 0000000..35feea1
--- /dev/null
+++ b/cmds/evemu-record/evdev.rs
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Wrappers for the Linux evdev APIs.
+
+use std::fs::File;
+use std::io;
+use std::mem;
+use std::os::fd::{AsRawFd, OwnedFd};
+use std::path::Path;
+
+use libc::c_int;
+use nix::sys::time::TimeVal;
+
+pub const SYN_CNT: usize = 0x10;
+pub const KEY_CNT: usize = 0x300;
+pub const REL_CNT: usize = 0x10;
+pub const ABS_CNT: usize = 0x40;
+pub const MSC_CNT: usize = 0x08;
+pub const SW_CNT: usize = 0x11;
+pub const LED_CNT: usize = 0x10;
+pub const SND_CNT: usize = 0x08;
+pub const REP_CNT: usize = 0x02;
+
+// Disable naming warnings, as these are supposed to match the EV_ constants in input-event-codes.h.
+#[allow(non_camel_case_types)]
+// Some of these types aren't referenced for evemu purposes, but are included for completeness.
+#[allow(dead_code)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[repr(u16)]
+pub enum EventType {
+ SYN = 0x00,
+ KEY = 0x01,
+ REL = 0x02,
+ ABS = 0x03,
+ MSC = 0x04,
+ SW = 0x05,
+ LED = 0x11,
+ SND = 0x12,
+ REP = 0x14,
+ FF = 0x15,
+ PWR = 0x16,
+ FF_STATUS = 0x17,
+}
+
+impl EventType {
+ fn code_count(&self) -> usize {
+ match self {
+ EventType::SYN => SYN_CNT,
+ EventType::KEY => KEY_CNT,
+ EventType::REL => REL_CNT,
+ EventType::ABS => ABS_CNT,
+ EventType::MSC => MSC_CNT,
+ EventType::SW => SW_CNT,
+ EventType::LED => LED_CNT,
+ EventType::SND => SND_CNT,
+ EventType::REP => REP_CNT,
+ _ => {
+ panic!("Event type {self:?} does not have a defined code count.");
+ }
+ }
+ }
+}
+
+pub const EVENT_TYPES_WITH_BITMAPS: [EventType; 7] = [
+ EventType::KEY,
+ EventType::REL,
+ EventType::ABS,
+ EventType::MSC,
+ EventType::SW,
+ EventType::LED,
+ EventType::SND,
+];
+
+const INPUT_PROP_CNT: usize = 32;
+
+/// The `ioctl_*!` macros create public functions by default, so this module makes them private.
+mod ioctl {
+ use nix::{ioctl_read, ioctl_read_buf};
+
+ ioctl_read!(eviocgid, b'E', 0x02, super::DeviceId);
+ ioctl_read_buf!(eviocgname, b'E', 0x06, u8);
+ ioctl_read_buf!(eviocgprop, b'E', 0x09, u8);
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct DeviceId {
+ pub bus_type: u16,
+ pub vendor: u16,
+ pub product: u16,
+ pub version: u16,
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct AbsoluteAxisInfo {
+ pub value: i32,
+ pub minimum: i32,
+ pub maximum: i32,
+ pub fuzz: i32,
+ pub flat: i32,
+ pub resolution: i32,
+}
+
+#[repr(C)]
+pub struct InputEvent {
+ pub time: TimeVal,
+ pub type_: u16,
+ pub code: u16,
+ pub value: i32,
+}
+
+impl InputEvent {
+ pub fn offset_time_by(&self, offset: TimeVal) -> InputEvent {
+ InputEvent { time: self.time - offset, ..*self }
+ }
+}
+
+impl Default for InputEvent {
+ fn default() -> Self {
+ InputEvent { time: TimeVal::new(0, 0), type_: 0, code: 0, value: 0 }
+ }
+}
+
+/// An object representing an input device using Linux's evdev protocol.
+pub struct Device {
+ fd: OwnedFd,
+}
+
+/// # Safety
+///
+/// `ioctl` must be safe to call with the given file descriptor and a pointer to a buffer of
+/// `initial_buf_size` `u8`s.
+unsafe fn buf_from_ioctl(
+ ioctl: unsafe fn(c_int, &mut [u8]) -> nix::Result<c_int>,
+ fd: &OwnedFd,
+ initial_buf_size: usize,
+) -> Result<Vec<u8>, nix::errno::Errno> {
+ let mut buf = vec![0; initial_buf_size];
+ // SAFETY:
+ // Here we're relying on the safety guarantees for `ioctl` made by the caller.
+ match unsafe { ioctl(fd.as_raw_fd(), buf.as_mut_slice()) } {
+ Ok(len) if len < 0 => {
+ panic!("ioctl returned invalid length {len}");
+ }
+ Ok(len) => {
+ buf.truncate(len as usize);
+ Ok(buf)
+ }
+ Err(err) => Err(err),
+ }
+}
+
+impl Device {
+ /// Opens a device from the evdev node at the given path.
+ pub fn open(path: &Path) -> io::Result<Device> {
+ Ok(Device { fd: OwnedFd::from(File::open(path)?) })
+ }
+
+ /// Returns the name of the device, as set by the relevant kernel driver.
+ pub fn name(&self) -> Result<String, nix::errno::Errno> {
+ // There's no official maximum length for evdev device names. The Linux HID driver
+ // currently supports names of at most 151 bytes (128 from the device plus a suffix of up
+ // to 23 bytes). 256 seems to be the buffer size most commonly used in evdev bindings, so
+ // we use it here.
+ //
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // The ioctl_read_buf macro prevents the retrieved data from overflowing the buffer created
+ // by buf_from_ioctl by passing in the size to the ioctl, meaning that the kernel's
+ // str_to_user function will truncate the string to that length.
+ let mut buf = unsafe { buf_from_ioctl(ioctl::eviocgname, &self.fd, 256)? };
+ assert!(!buf.is_empty(), "buf is too short for an empty null-terminated string");
+ assert_eq!(buf.pop().unwrap(), 0, "buf is not a null-terminated string");
+ Ok(String::from_utf8_lossy(buf.as_slice()).into_owned())
+ }
+
+ pub fn ids(&self) -> Result<DeviceId, nix::errno::Errno> {
+ let mut ids = DeviceId::default();
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // We know that the pointer to ids is valid because we just allocated it.
+ unsafe { ioctl::eviocgid(self.fd.as_raw_fd(), &mut ids) }.map(|_| ids)
+ }
+
+ pub fn properties_bitmap(&self) -> Result<Vec<u8>, nix::errno::Errno> {
+ let buf_size = (INPUT_PROP_CNT + 7) / 8;
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // The ioctl_read_buf macro prevents the retrieved data from overflowing the buffer created
+ // by buf_from_ioctl by passing in the size to the ioctl, meaning that the kernel's
+ // str_to_user function will truncate the string to that length.
+ unsafe { buf_from_ioctl(ioctl::eviocgprop, &self.fd, buf_size) }
+ }
+
+ pub fn bitmap_for_event_type(&self, event_type: EventType) -> nix::Result<Vec<u8>> {
+ let buf_size = (event_type.code_count() + 7) / 8;
+ let mut buf = vec![0; buf_size];
+
+ // The EVIOCGBIT ioctl can't be bound using ioctl_read_buf! like the others, since it uses
+ // part of its ioctl code as an additional parameter, for the event type. Hence this unsafe
+ // block is a manual expansion of ioctl_read_buf!.
+ //
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // We prevent the retrieved data from overflowing buf by passing in the size of buf to the
+ // ioctl, meaning that the kernel's str_to_user function will truncate the string to that
+ // length. We also panic if the ioctl returns a length longer than buf, hopefully before the
+ // overflow can do any damage.
+ match nix::errno::Errno::result(unsafe {
+ libc::ioctl(
+ self.fd.as_raw_fd(),
+ nix::request_code_read!(b'E', 0x20 + event_type as u16, buf.len())
+ as nix::sys::ioctl::ioctl_num_type,
+ buf.as_mut_ptr(),
+ )
+ }) {
+ Ok(len) if len < 0 => {
+ panic!("EVIOCGBIT returned invalid length {len} for event type {event_type:?}");
+ }
+ Ok(len) => {
+ buf.truncate(len as usize);
+ Ok(buf)
+ }
+ Err(err) => Err(err),
+ }
+ }
+
+ pub fn supported_axes_of_type(&self, event_type: EventType) -> nix::Result<Vec<u16>> {
+ let mut axes = Vec::new();
+ for (i, byte_ref) in self.bitmap_for_event_type(event_type)?.iter().enumerate() {
+ let mut byte = *byte_ref;
+ for j in 0..8 {
+ if byte & 1 == 1 {
+ axes.push((i * 8 + j) as u16);
+ }
+ byte >>= 1;
+ }
+ }
+ Ok(axes)
+ }
+
+ pub fn absolute_axis_info(&self, axis: u16) -> nix::Result<AbsoluteAxisInfo> {
+ let mut info = AbsoluteAxisInfo::default();
+ // The EVIOCGABS ioctl can't be bound using ioctl_read! since it uses part of its ioctl code
+ // as an additional parameter, for the axis code. Hence this unsafe block is a manual
+ // expansion of ioctl_read!.
+ //
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // We know that the pointer to info is valid because we just allocated it.
+ nix::errno::Errno::result(unsafe {
+ nix::libc::ioctl(
+ self.fd.as_raw_fd(),
+ nix::request_code_read!(b'E', 0x40 + axis, mem::size_of::<AbsoluteAxisInfo>()),
+ &mut info,
+ )
+ })
+ .map(|_| info)
+ }
+
+ pub fn read_event(&self) -> nix::Result<InputEvent> {
+ let mut event = InputEvent::default();
+
+ // SAFETY:
+ // We know that fd is a valid file descriptor as it comes from a File that we have open.
+ //
+ // We know that the pointer to event is valid because we just allocated it, and that the
+ // data structures match up because InputEvent is repr(C) and all its members are repr(C)
+ // or primitives that support all representations without niches.
+ nix::errno::Errno::result(unsafe {
+ libc::read(
+ self.fd.as_raw_fd(),
+ &mut event as *mut _ as *mut std::ffi::c_void,
+ mem::size_of::<InputEvent>(),
+ )
+ })
+ .map(|_| event)
+ }
+}
diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs
new file mode 100644
index 0000000..6f5deb9
--- /dev/null
+++ b/cmds/evemu-record/main.rs
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! A Rust implementation of the evemu-record command from the [FreeDesktop evemu suite][evemu] of
+//! tools.
+//!
+//! [evemu]: https://gitlab.freedesktop.org/libevdev/evemu
+
+use std::cmp;
+use std::error::Error;
+use std::fs;
+use std::io;
+use std::io::{BufRead, Write};
+use std::path::PathBuf;
+
+use clap::Parser;
+use nix::sys::time::TimeVal;
+
+mod evdev;
+
+/// Records evdev events from an input device in a format compatible with the FreeDesktop evemu
+/// library.
+#[derive(Parser, Debug)]
+struct Args {
+ /// The path to the input device to record. If omitted, offers a list of devices to choose from.
+ device: Option<PathBuf>,
+ /// The file to save the recording to. Defaults to standard output.
+ output_file: Option<PathBuf>,
+}
+
+fn get_choice(max: u32) -> u32 {
+ fn read_u32() -> Result<u32, std::num::ParseIntError> {
+ io::stdin().lock().lines().next().unwrap().unwrap().parse::<u32>()
+ }
+ let mut choice = read_u32();
+ while choice.is_err() || choice.clone().unwrap() > max {
+ eprint!("Enter a number between 0 and {max} inclusive: ");
+ choice = read_u32();
+ }
+ choice.unwrap()
+}
+
+fn pick_input_device() -> Result<PathBuf, io::Error> {
+ eprintln!("Available devices:");
+ let mut entries =
+ fs::read_dir("/dev/input")?.filter_map(|entry| entry.ok()).collect::<Vec<_>>();
+ entries.sort_by_key(|entry| entry.path());
+ let mut highest_number = 0;
+ for entry in entries {
+ let path = entry.path();
+ let file_name = path.file_name().unwrap().to_str().unwrap();
+ if path.is_dir() || !file_name.starts_with("event") {
+ continue;
+ }
+ let number = file_name.strip_prefix("event").unwrap().parse::<u32>();
+ if number.is_err() {
+ continue;
+ }
+ let number = number.unwrap();
+ match evdev::Device::open(path.as_path()) {
+ Ok(dev) => {
+ highest_number = cmp::max(highest_number, number);
+ eprintln!(
+ "{}:\t{}",
+ path.display(),
+ dev.name().unwrap_or("[could not read name]".to_string()),
+ );
+ }
+ Err(_) => {
+ eprintln!("Couldn't open {}", path.display());
+ }
+ }
+ }
+ eprint!("Select the device event number [0-{highest_number}]: ");
+ let choice = get_choice(highest_number);
+ Ok(PathBuf::from(format!("/dev/input/event{choice}")))
+}
+
+fn print_device_description(
+ device: &evdev::Device,
+ output: &mut impl Write,
+) -> Result<(), Box<dyn Error>> {
+ // TODO(b/302297266): report LED and SW states, then bump the version to EVEMU 1.3.
+ writeln!(output, "# EVEMU 1.2")?;
+ writeln!(output, "N: {}", device.name()?)?;
+
+ let ids = device.ids()?;
+ writeln!(
+ output,
+ "I: {:04x} {:04x} {:04x} {:04x}",
+ ids.bus_type, ids.vendor, ids.product, ids.version,
+ )?;
+
+ fn print_in_8_byte_chunks(
+ output: &mut impl Write,
+ prefix: &str,
+ data: &Vec<u8>,
+ ) -> Result<(), io::Error> {
+ for (i, byte) in data.iter().enumerate() {
+ if i % 8 == 0 {
+ write!(output, "{prefix}")?;
+ }
+ write!(output, " {:02x}", byte)?;
+ if (i + 1) % 8 == 0 {
+ writeln!(output)?;
+ }
+ }
+ if data.len() % 8 != 0 {
+ for _ in (data.len() % 8)..8 {
+ write!(output, " 00")?;
+ }
+ writeln!(output)?;
+ }
+ Ok(())
+ }
+
+ let props = device.properties_bitmap()?;
+ print_in_8_byte_chunks(output, "P:", &props)?;
+
+ // The SYN event type can't be queried through the EVIOCGBIT ioctl, so just hard-code it to
+ // SYN_REPORT, SYN_CONFIG, and SYN_DROPPED.
+ writeln!(output, "B: 00 0b 00 00 00 00 00 00 00")?;
+ for event_type in evdev::EVENT_TYPES_WITH_BITMAPS {
+ let bits = device.bitmap_for_event_type(event_type)?;
+ print_in_8_byte_chunks(output, format!("B: {:02x}", event_type as u16).as_str(), &bits)?;
+ }
+
+ for axis in device.supported_axes_of_type(evdev::EventType::ABS)? {
+ let info = device.absolute_axis_info(axis)?;
+ writeln!(
+ output,
+ "A: {axis:02x} {} {} {} {} {}",
+ info.minimum, info.maximum, info.fuzz, info.flat, info.resolution
+ )?;
+ }
+ Ok(())
+}
+
+fn print_events(device: &evdev::Device, output: &mut impl Write) -> Result<(), Box<dyn Error>> {
+ fn print_event(output: &mut impl Write, event: &evdev::InputEvent) -> Result<(), io::Error> {
+ // TODO(b/302297266): Translate events into human-readable names and add those as comments.
+ writeln!(
+ output,
+ "E: {}.{:06} {:04x} {:04x} {:04}",
+ event.time.tv_sec(),
+ event.time.tv_usec(),
+ event.type_,
+ event.code,
+ event.value,
+ )?;
+ Ok(())
+ }
+ let event = device.read_event()?;
+ // Due to a bug in the C implementation of evemu-play [0] that has since become part of the API,
+ // the timestamp of the first event in a recording shouldn't be exactly 0.0 seconds, so offset
+ // it by 1µs.
+ //
+ // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d
+ let start_time = event.time - TimeVal::new(0, 1);
+ print_event(output, &event.offset_time_by(start_time))?;
+ loop {
+ let event = device.read_event()?;
+ print_event(output, &event.offset_time_by(start_time))?;
+ }
+}
+
+fn main() -> Result<(), Box<dyn Error>> {
+ let args = Args::parse();
+
+ let device_path = args.device.unwrap_or_else(|| pick_input_device().unwrap());
+
+ let device = evdev::Device::open(device_path.as_path())?;
+ let mut output = match args.output_file {
+ Some(path) => Box::new(fs::File::create(path)?) as Box<dyn Write>,
+ None => Box::new(io::stdout().lock()),
+ };
+ print_device_description(&device, &mut output)?;
+ print_events(&device, &mut output)?;
+ Ok(())
+}
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index ac101ec..334bae4 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -34,7 +34,6 @@
"unique_file.cpp",
"utils.cpp",
"utils_default.cpp",
- "view_compiler.cpp",
":installd_aidl",
],
shared_libs: [
@@ -254,7 +253,6 @@
"unique_file.cpp",
"utils.cpp",
"utils_default.cpp",
- "view_compiler.cpp",
],
static_libs: [
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 073d0c4..e14af77 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -69,7 +69,6 @@
#include "installd_deps.h"
#include "otapreopt_utils.h"
#include "utils.h"
-#include "view_compiler.h"
#include "CacheTracker.h"
#include "CrateManager.h"
@@ -472,6 +471,49 @@
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
@@ -480,56 +522,56 @@
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
@@ -3258,17 +3300,6 @@
return ok();
}
-binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath,
- const std::string& packageName,
- const std ::string& outDexFile, int uid,
- bool* _aidl_return) {
- const char* apk_path = apkPath.c_str();
- const char* package_name = packageName.c_str();
- const char* out_dex_file = outDexFile.c_str();
- *_aidl_return = android::installd::view_compiler(apk_path, package_name, out_dex_file, uid);
- return *_aidl_return ? ok() : error("viewcompiler failed");
-}
-
binder::Status InstalldNativeService::linkNativeLibraryDirectory(
const std::optional<std::string>& uuid, const std::string& packageName,
const std::string& nativeLibPath32, int32_t userId) {
@@ -3295,7 +3326,7 @@
}
char *con = nullptr;
- if (lgetfilecon(pkgdir, &con) < 0) {
+ if (::lgetfilecon(pkgdir, &con) < 0) {
return error("Failed to lgetfilecon " + _pkgdir);
}
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 1ec092d..1b56144 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -146,9 +146,6 @@
binder::Status controlDexOptBlocking(bool block);
- binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName,
- const std::string& outDexFile, int uid, bool* _aidl_return);
-
binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
binder::Status mergeProfiles(int32_t uid, const std::string& packageName,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 8893e38..f5a7709 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -70,8 +70,6 @@
// Blocks (when block is true) or unblock (when block is false) dexopt.
// Blocking also invloves cancelling the currently running dexopt.
void controlDexOptBlocking(boolean block);
- boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName,
- @utf8InCpp String outDexFile, int uid);
void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 822ab7f..8eb7458 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -514,6 +514,8 @@
// Make sure dex2oat is run with background priority.
dexopt_flags |= DEXOPT_BOOTCOMPLETE | DEXOPT_IDLE_BACKGROUND_JOB;
+ parameters_.compilation_reason = "ab-ota";
+
int res = dexopt(parameters_.apk_path,
parameters_.uid,
parameters_.pkgName,
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c40caf5..c86adef 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -353,7 +353,7 @@
// Now go on and read dexopt lines from stdin and pass them on to otapreopt.
int count = 1;
- for (std::array<char, 1000> linebuf;
+ for (std::array<char, 10000> linebuf;
std::cin.clear(), std::cin.getline(&linebuf[0], linebuf.size()); ++count) {
// Subtract one from gcount() since getline() counts the newline.
std::string line(&linebuf[0], std::cin.gcount() - 1);
diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp
deleted file mode 100644
index 8c000a1..0000000
--- a/cmds/installd/view_compiler.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2019 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 "view_compiler.h"
-
-#include <string>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "utils.h"
-
-#include "android-base/logging.h"
-#include "android-base/stringprintf.h"
-#include "android-base/unique_fd.h"
-
-namespace android {
-namespace installd {
-
-namespace {
-
-using ::android::base::unique_fd;
-
-constexpr int kTimeoutMs = 300000;
-
-} // namespace
-
-bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file,
- int uid) {
- CHECK(apk_path != nullptr);
- CHECK(package_name != nullptr);
- CHECK(out_dex_file != nullptr);
-
- // viewcompiler won't have permission to open anything, so we have to open the files first
- // and pass file descriptors.
-
- // Open input file
- unique_fd infd{open(apk_path, O_RDONLY)}; // NOLINT(android-cloexec-open)
- if (infd.get() < 0) {
- PLOG(ERROR) << "Could not open input file: " << apk_path;
- return false;
- }
-
- // Set up output file. viewcompiler can't open outputs by fd, but it can write to stdout, so
- // we close stdout and open it towards the right output.
- unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644)};
- if (outfd.get() < 0) {
- PLOG(ERROR) << "Could not open output file: " << out_dex_file;
- return false;
- }
- if (fchmod(outfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) {
- PLOG(ERROR) << "Could not change output file permissions";
- return false;
- }
- if (dup2(outfd, STDOUT_FILENO) < 0) {
- PLOG(ERROR) << "Could not duplicate output file descriptor";
- return false;
- }
-
- // Prepare command line arguments for viewcompiler
- std::string args[] = {"/system/bin/viewcompiler",
- "--apk",
- "--infd",
- android::base::StringPrintf("%d", infd.get()),
- "--dex",
- "--package",
- package_name};
- char* const argv[] = {const_cast<char*>(args[0].c_str()), const_cast<char*>(args[1].c_str()),
- const_cast<char*>(args[2].c_str()), const_cast<char*>(args[3].c_str()),
- const_cast<char*>(args[4].c_str()), const_cast<char*>(args[5].c_str()),
- const_cast<char*>(args[6].c_str()), nullptr};
-
- pid_t pid = fork();
- if (pid == 0) {
- // Now that we've opened the files we need, drop privileges.
- drop_capabilities(uid);
- execv("/system/bin/viewcompiler", argv);
- _exit(1);
- }
-
- int return_code = wait_child_with_timeout(pid, kTimeoutMs);
- if (!WIFEXITED(return_code)) {
- LOG(WARNING) << "viewcompiler failed for " << package_name << ":" << apk_path;
- if (WTERMSIG(return_code) == SIGKILL) {
- // If the subprocess is killed while it's writing to the file, the file is likely
- // corrupted, so we should remove it.
- remove_file_at_fd(outfd.get());
- }
- return false;
- }
- return WEXITSTATUS(return_code) == 0;
-}
-
-} // namespace installd
-} // namespace android
diff --git a/cmds/installd/view_compiler.h b/cmds/installd/view_compiler.h
deleted file mode 100644
index aa141ca..0000000
--- a/cmds/installd/view_compiler.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2019 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 VIEW_COMPILER_H_
-#define VIEW_COMPILER_H_
-
-namespace android {
-namespace installd {
-
-bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file,
- int uid);
-
-} // namespace installd
-} // namespace android
-
-#endif // VIEW_COMPILER_H_
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index e286a84..89736ec 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -131,6 +131,24 @@
}
prebuilt_etc {
+ name: "android.hardware.se.omapi.ese.prebuilt.xml",
+ src: "android.hardware.se.omapi.ese.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.se.omapi.sd.prebuilt.xml",
+ src: "android.hardware.se.omapi.sd.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
+ name: "android.hardware.se.omapi.uicc.prebuilt.xml",
+ src: "android.hardware.se.omapi.uicc.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.sensor.accelerometer_limited_axes_uncalibrated.prebuilt.xml",
src: "android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -353,6 +371,12 @@
}
prebuilt_etc {
+ name: "android.software.opengles.deqp.level-2024-03-01.prebuilt.xml",
+ src: "android.software.opengles.deqp.level-2024-03-01.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.software.opengles.deqp.level-latest.prebuilt.xml",
src: "android.software.opengles.deqp.level-latest.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -389,6 +413,12 @@
}
prebuilt_etc {
+ name: "android.software.vulkan.deqp.level-2024-03-01.prebuilt.xml",
+ src: "android.software.vulkan.deqp.level-2024-03-01.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.software.vulkan.deqp.level-latest.prebuilt.xml",
src: "android.software.vulkan.deqp.level-latest.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.software.opengles.deqp.level-2024-03-01.xml b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml
new file mode 100644
index 0000000..4eeed2a
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+ dEQP tests associated with date 2023-03-01 (0x07E70301). -->
+<permissions>
+ <feature name="android.software.opengles.deqp.level" version="132645633" />
+</permissions>
diff --git a/data/etc/android.software.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml
index bd15eb6..62bb101 100644
--- a/data/etc/android.software.opengles.deqp.level-latest.xml
+++ b/data/etc/android.software.opengles.deqp.level-latest.xml
@@ -17,5 +17,5 @@
<!-- This is the standard feature indicating that the device passes OpenGL ES
dEQP tests associated with the most recent level for this Android version. -->
<permissions>
- <feature name="android.software.opengles.deqp.level" version="132580097" />
+ <feature name="android.software.opengles.deqp.level" version="132645633" />
</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml
new file mode 100644
index 0000000..8b2b4c8
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
+ tests associated with date 2023-03-01 (0x07E70301). -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132645633" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml
index 87be070..0fc12b3 100644
--- a/data/etc/android.software.vulkan.deqp.level-latest.xml
+++ b/data/etc/android.software.vulkan.deqp.level-latest.xml
@@ -17,5 +17,5 @@
<!-- This is the standard feature indicating that the device passes Vulkan
dEQP tests associated with the most recent level for this Android version. -->
<permissions>
- <feature name="android.software.vulkan.deqp.level" version="132580097" />
+ <feature name="android.software.vulkan.deqp.level" version="132645633" />
</permissions>
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index ba8b02d..9d2c791 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -60,6 +60,27 @@
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 @@
*
* @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 @@
* 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 @@
* @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 @@
* 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 @@
* 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 @@
* @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 @@
* 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 @@
* 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/input/Input.h b/include/input/Input.h
index bd544b5..1c4ea6b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -1138,6 +1138,24 @@
std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool;
};
+/**
+ * An input event factory implementation that simply creates the input events on the heap, when
+ * needed. The caller is responsible for destroying the returned references.
+ * It is recommended that the caller wrap these return values into std::unique_ptr.
+ */
+class DynamicInputEventFactory : public InputEventFactoryInterface {
+public:
+ explicit DynamicInputEventFactory(){};
+ ~DynamicInputEventFactory(){};
+
+ KeyEvent* createKeyEvent() override { return new KeyEvent(); };
+ MotionEvent* createMotionEvent() override { return new MotionEvent(); };
+ FocusEvent* createFocusEvent() override { return new FocusEvent(); };
+ CaptureEvent* createCaptureEvent() override { return new CaptureEvent(); };
+ DragEvent* createDragEvent() override { return new DragEvent(); };
+ TouchModeEvent* createTouchModeEvent() override { return new TouchModeEvent(); };
+};
+
/*
* Describes a unique request to enable or disable Pointer Capture.
*/
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 9c0c10e..2d23b97 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -160,4 +160,90 @@
std::vector<PointerBuilder> mPointers;
};
+class KeyEventBuilder {
+public:
+ KeyEventBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mDownTime = mEventTime;
+ }
+
+ KeyEventBuilder(const KeyEvent& event) {
+ mAction = event.getAction();
+ mDeviceId = event.getDeviceId();
+ mSource = event.getSource();
+ mDownTime = event.getDownTime();
+ mEventTime = event.getEventTime();
+ mDisplayId = event.getDisplayId();
+ mFlags = event.getFlags();
+ mKeyCode = event.getKeyCode();
+ mScanCode = event.getScanCode();
+ mMetaState = event.getMetaState();
+ mRepeatCount = event.getRepeatCount();
+ }
+
+ KeyEventBuilder& deviceId(int32_t deviceId) {
+ mDeviceId = deviceId;
+ return *this;
+ }
+
+ KeyEventBuilder& downTime(nsecs_t downTime) {
+ mDownTime = downTime;
+ return *this;
+ }
+
+ KeyEventBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ KeyEventBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ KeyEventBuilder& policyFlags(int32_t policyFlags) {
+ mPolicyFlags = policyFlags;
+ return *this;
+ }
+
+ KeyEventBuilder& addFlag(uint32_t flags) {
+ mFlags |= flags;
+ return *this;
+ }
+
+ KeyEventBuilder& keyCode(int32_t keyCode) {
+ mKeyCode = keyCode;
+ return *this;
+ }
+
+ KeyEventBuilder& repeatCount(int32_t repeatCount) {
+ mRepeatCount = repeatCount;
+ return *this;
+ }
+
+ KeyEvent build() const {
+ KeyEvent event{};
+ event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
+ mAction, mFlags, mKeyCode, mScanCode, mMetaState, mRepeatCount, mDownTime,
+ mEventTime);
+ return event;
+ }
+
+private:
+ int32_t mAction;
+ int32_t mDeviceId = DEFAULT_DEVICE_ID;
+ uint32_t mSource;
+ nsecs_t mDownTime;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
+ int32_t mFlags{0};
+ int32_t mKeyCode{AKEYCODE_UNKNOWN};
+ int32_t mScanCode{0};
+ int32_t mMetaState{AMETA_NONE};
+ int32_t mRepeatCount{0};
+};
+
} // namespace android
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index b2e8baa..dfcf766 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -146,7 +146,7 @@
#ifdef __linux__
/* Reads a key map from a parcel. */
- static std::shared_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
+ static std::unique_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
/* Writes a key map to a parcel. */
void writeToParcel(Parcel* parcel) const;
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 2e99495..ee74455 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -98,7 +98,7 @@
void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position);
// Adds movement information for all pointers in a MotionEvent, including historical samples.
- void addMovement(const MotionEvent* event);
+ void addMovement(const MotionEvent& event);
// Returns the velocity of the specified pointer id and axis in position units per second.
// Returns empty optional if there is insufficient movement information for the pointer, or if
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 620c23c..f90c618 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -251,13 +251,15 @@
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 f22e90a..301dbf6 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -19,7 +19,6 @@
#include <atomic>
#include <set>
-#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
@@ -30,7 +29,6 @@
#include <binder/RecordedTransaction.h>
#include <binder/RpcServer.h>
#include <pthread.h>
-#include <utils/misc.h>
#include <inttypes.h>
#include <stdio.h>
@@ -271,7 +269,7 @@
bool mInheritRt = false;
// for below objects
- Mutex mLock;
+ RpcMutex mLock;
std::set<sp<RpcServerLink>> mRpcServerLinks;
BpBinder::ObjectManager mObjects;
@@ -307,9 +305,9 @@
return PERMISSION_DENIED;
}
Extras* e = getOrCreateExtras();
- AutoMutex lock(e->mLock);
+ RpcMutexUniqueLock lock(e->mLock);
if (mRecordingOn) {
- LOG(INFO) << "Could not start Binder recording. Another is already in progress.";
+ ALOGI("Could not start Binder recording. Another is already in progress.");
return INVALID_OPERATION;
} else {
status_t readStatus = data.readUniqueFileDescriptor(&(e->mRecordingFd));
@@ -317,7 +315,7 @@
return readStatus;
}
mRecordingOn = true;
- LOG(INFO) << "Started Binder recording.";
+ ALOGI("Started Binder recording.");
return NO_ERROR;
}
}
@@ -337,14 +335,14 @@
return PERMISSION_DENIED;
}
Extras* e = getOrCreateExtras();
- AutoMutex lock(e->mLock);
+ RpcMutexUniqueLock lock(e->mLock);
if (mRecordingOn) {
e->mRecordingFd.reset();
mRecordingOn = false;
- LOG(INFO) << "Stopped Binder recording.";
+ ALOGI("Stopped Binder recording.");
return NO_ERROR;
} else {
- LOG(INFO) << "Could not stop Binder recording. One is not in progress.";
+ ALOGI("Could not stop Binder recording. One is not in progress.");
return INVALID_OPERATION;
}
}
@@ -378,11 +376,11 @@
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: {
@@ -405,7 +403,7 @@
if (kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION) [[unlikely]] {
Extras* e = mExtras.load(std::memory_order_acquire);
- AutoMutex lock(e->mLock);
+ RpcMutexUniqueLock lock(e->mLock);
if (mRecordingOn) {
Parcel emptyReply;
timespec ts;
@@ -415,10 +413,10 @@
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.");
}
}
}
@@ -452,7 +450,7 @@
Extras* e = getOrCreateExtras();
LOG_ALWAYS_FATAL_IF(!e, "no memory");
- AutoMutex _l(e->mLock);
+ RpcMutexUniqueLock _l(e->mLock);
return e->mObjects.attach(objectID, object, cleanupCookie, func);
}
@@ -461,7 +459,7 @@
Extras* e = mExtras.load(std::memory_order_acquire);
if (!e) return nullptr;
- AutoMutex _l(e->mLock);
+ RpcMutexUniqueLock _l(e->mLock);
return e->mObjects.find(objectID);
}
@@ -469,7 +467,7 @@
Extras* e = mExtras.load(std::memory_order_acquire);
if (!e) return nullptr;
- AutoMutex _l(e->mLock);
+ RpcMutexUniqueLock _l(e->mLock);
return e->mObjects.detach(objectID);
}
@@ -477,7 +475,7 @@
Extras* e = getOrCreateExtras();
LOG_ALWAYS_FATAL_IF(!e, "no memory");
- AutoMutex _l(e->mLock);
+ RpcMutexUniqueLock _l(e->mLock);
doWithLock();
}
@@ -485,7 +483,7 @@
const void* makeArgs) {
Extras* e = getOrCreateExtras();
LOG_ALWAYS_FATAL_IF(!e, "no memory");
- AutoMutex _l(e->mLock);
+ RpcMutexUniqueLock _l(e->mLock);
return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
}
@@ -692,7 +690,7 @@
auto weakThis = wp<BBinder>::fromExisting(this);
Extras* e = getOrCreateExtras();
- AutoMutex _l(e->mLock);
+ RpcMutexUniqueLock _l(e->mLock);
auto rpcServer = RpcServer::make();
LOG_ALWAYS_FATAL_IF(rpcServer == nullptr, "RpcServer::make returns null");
auto link = sp<RpcServerLink>::make(rpcServer, keepAliveBinder, weakThis);
@@ -706,7 +704,7 @@
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);
@@ -716,7 +714,7 @@
void BBinder::removeRpcServerLink(const sp<RpcServerLink>& link) {
Extras* e = mExtras.load(std::memory_order_acquire);
if (!e) return;
- AutoMutex _l(e->mLock);
+ RpcMutexUniqueLock _l(e->mLock);
(void)e->mRpcServerLinks.erase(link);
}
@@ -724,20 +722,20 @@
{
if (!wasParceled()) {
if (getExtension()) {
- ALOGW("Binder %p destroyed with extension attached before being parceled.", this);
+ ALOGW("Binder %p destroyed with extension attached before being parceled.", this);
}
if (isRequestingSid()) {
- ALOGW("Binder %p destroyed when requesting SID before being parceled.", this);
+ ALOGW("Binder %p destroyed when requesting SID before being parceled.", this);
}
if (isInheritRt()) {
- ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this);
+ ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this);
}
#ifdef __linux__
if (getMinSchedulerPolicy() != SCHED_NORMAL) {
- ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
+ ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
}
if (getMinSchedulerPriority() != 0) {
- ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
+ ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
}
#endif // __linux__
}
@@ -753,7 +751,7 @@
{
switch (code) {
case INTERFACE_TRANSACTION:
- CHECK(reply != nullptr);
+ LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
reply->writeString16(getInterfaceDescriptor());
return NO_ERROR;
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 3bc4f92..49038b1 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -23,7 +23,6 @@
#include <binder/IResultReceiver.h>
#include <binder/RpcSession.h>
#include <binder/Stability.h>
-#include <utils/Log.h>
#include <stdio.h>
@@ -38,7 +37,7 @@
// ---------------------------------------------------------------------------
-Mutex BpBinder::sTrackingLock;
+RpcMutex BpBinder::sTrackingLock;
std::unordered_map<int32_t, uint32_t> BpBinder::sTrackingMap;
std::unordered_map<int32_t, uint32_t> BpBinder::sLastLimitCallbackMap;
int BpBinder::sNumTrackedUids = 0;
@@ -163,7 +162,7 @@
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
trackedUid = IPCThreadState::self()->getCallingUid();
- AutoMutex _l(sTrackingLock);
+ RpcMutexUniqueLock _l(sTrackingLock);
uint32_t trackedValue = sTrackingMap[trackedUid];
if (trackedValue & LIMIT_REACHED_MASK) [[unlikely]] {
if (sBinderProxyThrottleCreate) {
@@ -276,7 +275,7 @@
}
bool BpBinder::isDescriptorCached() const {
- Mutex::Autolock _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
return mDescriptorCache.c_str() != kDescriptorUninit.c_str();
}
@@ -292,7 +291,7 @@
status_t err = thiz->transact(INTERFACE_TRANSACTION, data, &reply);
if (err == NO_ERROR) {
String16 res(reply.readString16());
- Mutex::Autolock _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
// mDescriptorCache could have been assigned while the lock was
// released.
if (mDescriptorCache.c_str() == kDescriptorUninit.c_str()) mDescriptorCache = res;
@@ -385,7 +384,7 @@
status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
}
if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) {
- Mutex::Autolock _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d",
data.dataSize(), String8(mDescriptorCache).c_str(), code);
}
@@ -431,7 +430,7 @@
"linkToDeath(): recipient must be non-NULL");
{
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
if (!mObitsSent) {
if (!mObituaries) {
@@ -467,7 +466,7 @@
return INVALID_OPERATION;
}
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
if (mObitsSent) {
return DEAD_OBJECT;
@@ -555,30 +554,30 @@
void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie,
object_cleanup_func func) {
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
return mObjects.attach(objectID, object, cleanupCookie, func);
}
void* BpBinder::findObject(const void* objectID) const
{
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
return mObjects.find(objectID);
}
void* BpBinder::detachObject(const void* objectID) {
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
return mObjects.detach(objectID);
}
void BpBinder::withLock(const std::function<void()>& doWithLock) {
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
doWithLock();
}
sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make,
const void* makeArgs) {
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
return mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
}
@@ -602,7 +601,7 @@
IPCThreadState* ipc = IPCThreadState::self();
if (mTrackedUid >= 0) {
- AutoMutex _l(sTrackingLock);
+ RpcMutexUniqueLock _l(sTrackingLock);
uint32_t trackedValue = sTrackingMap[mTrackedUid];
if ((trackedValue & COUNTING_VALUE_MASK) == 0) [[unlikely]] {
ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this,
@@ -702,7 +701,7 @@
uint32_t BpBinder::getBinderProxyCount(uint32_t uid)
{
- AutoMutex _l(sTrackingLock);
+ RpcMutexUniqueLock _l(sTrackingLock);
auto it = sTrackingMap.find(uid);
if (it != sTrackingMap.end()) {
return it->second & COUNTING_VALUE_MASK;
@@ -717,7 +716,7 @@
void BpBinder::getCountByUid(Vector<uint32_t>& uids, Vector<uint32_t>& counts)
{
- AutoMutex _l(sTrackingLock);
+ RpcMutexUniqueLock _l(sTrackingLock);
uids.setCapacity(sTrackingMap.size());
counts.setCapacity(sTrackingMap.size());
for (const auto& it : sTrackingMap) {
@@ -731,12 +730,12 @@
void BpBinder::setCountByUidEnabled(bool enable) { sCountByUidEnabled.store(enable); }
void BpBinder::setLimitCallback(binder_proxy_limit_callback cb) {
- AutoMutex _l(sTrackingLock);
+ RpcMutexUniqueLock _l(sTrackingLock);
sLimitCallback = cb;
}
void BpBinder::setBinderProxyCountWatermarks(int high, int low) {
- AutoMutex _l(sTrackingLock);
+ RpcMutexUniqueLock _l(sTrackingLock);
sBinderProxyCountHighWatermark = high;
sBinderProxyCountLowWatermark = low;
}
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index c6e4fb3..7ae616e 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -19,8 +19,6 @@
#include <binder/ProcessState.h>
-#include <utils/misc.h>
-
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index 8ee6cb0..a1fbbf3 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -21,12 +21,15 @@
#include <poll.h>
-#include <android-base/macros.h>
-#include <android-base/scopeguard.h>
+#include <binder/Functional.h>
#include "RpcState.h"
+#include "Utils.h"
+
namespace android {
+using namespace android::binder::impl;
+
std::unique_ptr<FdTrigger> FdTrigger::make() {
auto ret = std::make_unique<FdTrigger>();
#ifndef BINDER_RPC_SINGLE_THREADED
@@ -74,10 +77,9 @@
"Only one thread should be polling on Fd!");
transportFd.setPollingState(true);
- auto pollingStateGuard =
- android::base::make_scope_guard([&]() { transportFd.setPollingState(false); });
+ auto pollingStateGuard = make_scope_guard([&]() { transportFd.setPollingState(false); });
- int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+ int ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1));
if (ret < 0) {
return -errno;
}
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index 5fbf290..dba1dc9 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -17,7 +17,6 @@
#include <memory>
-#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <utils/Errors.h>
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp
index 2780bd4..dea2603 100644
--- a/libs/binder/IInterface.cpp
+++ b/libs/binder/IInterface.cpp
@@ -15,7 +15,6 @@
*/
#define LOG_TAG "IInterface"
-#include <utils/Log.h>
#include <binder/IInterface.h>
namespace android {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index da58251..9341eff 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -22,7 +22,6 @@
#include <binder/BpBinder.h>
#include <binder/TextOutput.h>
-#include <android-base/macros.h>
#include <cutils/sched_policy.h>
#include <utils/CallStack.h>
#include <utils/Log.h>
@@ -395,7 +394,9 @@
}
void IPCThreadState::checkContextIsBinderForUse(const char* use) const {
- if (LIKELY(mServingStackPointerGuard == nullptr)) return;
+ if (mServingStackPointerGuard == nullptr) [[likely]] {
+ return;
+ }
if (!mServingStackPointer || mServingStackPointerGuard->address < mServingStackPointer) {
LOG_ALWAYS_FATAL("In context %s, %s does not make sense (binder sp: %p, guard: %p).",
@@ -832,7 +833,7 @@
}
if ((flags & TF_ONE_WAY) == 0) {
- if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) {
+ if (mCallRestriction != ProcessState::CallRestriction::NONE) [[unlikely]] {
if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) {
ALOGE("Process making non-oneway call (code: %u) but is restricted.", code);
CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(),
@@ -842,13 +843,13 @@
}
}
- #if 0
+#if 0
if (code == 4) { // relayout
ALOGI(">>>>>> CALLING transaction 4");
} else {
ALOGI(">>>>>> CALLING transaction %d", code);
}
- #endif
+#endif
if (reply) {
err = waitForResponse(reply);
} else {
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index cd92217..60ece72 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -18,7 +18,6 @@
#include <binder/IResultReceiver.h>
-#include <utils/Log.h>
#include <binder/Parcel.h>
#include <utils/String8.h>
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 6034f2b..fe566fc 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -200,7 +200,7 @@
}
bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logPermissionFailure) {
- static Mutex gPermissionControllerLock;
+ static std::mutex gPermissionControllerLock;
static sp<IPermissionController> gPermissionController;
sp<IPermissionController> pc;
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index 5b1cb7e..95bdbb4 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -155,7 +155,7 @@
void dump_l(String8& res, const char* what) const;
static const int kMemoryAlign;
- mutable Mutex mLock;
+ mutable std::mutex mLock;
LinkedList<chunk_t> mList;
size_t mHeapSize;
};
@@ -305,14 +305,14 @@
size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags)
{
- Mutex::Autolock _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
ssize_t offset = alloc(size, flags);
return offset;
}
status_t SimpleBestFitAllocator::deallocate(size_t offset)
{
- Mutex::Autolock _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
chunk_t const * const freed = dealloc(offset);
if (freed) {
return NO_ERROR;
@@ -420,7 +420,7 @@
void SimpleBestFitAllocator::dump(const char* what) const
{
- Mutex::Autolock _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
dump_l(what);
}
@@ -434,7 +434,7 @@
void SimpleBestFitAllocator::dump(String8& result,
const char* what) const
{
- Mutex::Autolock _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
dump_l(result, what);
}
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index 8dc1f6a..bb7caa9 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -18,14 +18,13 @@
#include <stddef.h>
#include <cstdint>
-#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <binder/RpcTransport.h>
#include <utils/Errors.h>
namespace android::binder::os {
-android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
+status_t setNonBlocking(android::base::borrowed_fd fd);
status_t getRandomBytes(uint8_t* data, size_t size);
diff --git a/libs/binder/OS_unix_base.cpp b/libs/binder/OS_unix_base.cpp
index 81933d5..a3cf117 100644
--- a/libs/binder/OS_unix_base.cpp
+++ b/libs/binder/OS_unix_base.cpp
@@ -15,29 +15,29 @@
*/
#include "OS.h"
+#include "Utils.h"
#include <android-base/file.h>
#include <binder/RpcTransportRaw.h>
#include <log/log.h>
#include <string.h>
-using android::base::ErrnoError;
-using android::base::Result;
-
namespace android::binder::os {
// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
constexpr size_t kMaxFdsPerMsg = 253;
-Result<void> setNonBlocking(android::base::borrowed_fd fd) {
+status_t setNonBlocking(android::base::borrowed_fd fd) {
int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
if (flags == -1) {
- return ErrnoError() << "Could not get flags for fd";
+ PLOGE("Failed setNonBlocking: Could not get flags for fd");
+ return -errno;
}
if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) {
- return ErrnoError() << "Could not set non-blocking flag for fd";
+ PLOGE("Failed setNonBlocking: Could not set non-blocking flag for fd");
+ return -errno;
}
- return {};
+ return OK;
}
status_t getRandomBytes(uint8_t* data, size_t size) {
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a3ff7d2..3349402 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "Parcel"
//#define LOG_NDEBUG 0
+#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -32,6 +33,7 @@
#include <binder/Binder.h>
#include <binder/BpBinder.h>
+#include <binder/Functional.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
@@ -39,15 +41,11 @@
#include <binder/Status.h>
#include <binder/TextOutput.h>
-#include <android-base/scopeguard.h>
#ifndef BINDER_DISABLE_BLOB
#include <cutils/ashmem.h>
#endif
-#include <utils/Flattenable.h>
-#include <utils/Log.h>
#include <utils/String16.h>
#include <utils/String8.h>
-#include <utils/misc.h>
#include "OS.h"
#include "RpcState.h"
@@ -98,6 +96,8 @@
namespace android {
+using namespace android::binder::impl;
+
// many things compile this into prebuilts on the stack
#ifdef __LP64__
static_assert(sizeof(Parcel) == 120);
@@ -627,7 +627,7 @@
}
const size_t savedDataPos = mDataPos;
- base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; };
+ auto scopeGuard = make_scope_guard([&]() { mDataPos = savedDataPos; });
rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size());
if (otherRpcFields->mFds != nullptr) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 8ec4af9..0344eb0 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -18,10 +18,9 @@
#include <binder/ProcessState.h>
-#include <android-base/result.h>
-#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <binder/BpBinder.h>
+#include <binder/Functional.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Stability.h>
@@ -32,6 +31,7 @@
#include <utils/Thread.h>
#include "Static.h"
+#include "Utils.h"
#include "binder_module.h"
#include <errno.h>
@@ -60,6 +60,8 @@
namespace android {
+using namespace android::binder::impl;
+
class PoolThread : public Thread
{
public:
@@ -189,7 +191,7 @@
void ProcessState::startThreadPool()
{
- AutoMutex _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
if (!mThreadPoolStarted) {
if (mMaxThreads == 0) {
// see also getThreadPoolMaxTotalThreadCount
@@ -203,7 +205,7 @@
bool ProcessState::becomeContextManager()
{
- AutoMutex _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
flat_binder_object obj {
.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
@@ -310,7 +312,7 @@
{
sp<IBinder> result;
- AutoMutex _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
if (handle == 0 && the_context_object != nullptr) return the_context_object;
@@ -374,7 +376,7 @@
void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
{
- AutoMutex _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
@@ -430,7 +432,7 @@
size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
pthread_mutex_lock(&mThreadCountLock);
- base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); };
+ auto detachGuard = make_scope_guard([&]() { pthread_mutex_unlock(&mThreadCountLock); });
if (mThreadPoolStarted) {
LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1,
@@ -512,31 +514,31 @@
return mDriverName;
}
-static base::Result<int> open_driver(const char* driver) {
- int fd = open(driver, O_RDWR | O_CLOEXEC);
- if (fd < 0) {
- return base::ErrnoError() << "Opening '" << driver << "' failed";
+static base::unique_fd open_driver(const char* driver) {
+ auto fd = base::unique_fd(open(driver, O_RDWR | O_CLOEXEC));
+ if (!fd.ok()) {
+ PLOGE("Opening '%s' failed", driver);
+ return {};
}
int vers = 0;
- status_t result = ioctl(fd, BINDER_VERSION, &vers);
+ int result = ioctl(fd.get(), BINDER_VERSION, &vers);
if (result == -1) {
- close(fd);
- return base::ErrnoError() << "Binder ioctl to obtain version failed";
+ PLOGE("Binder ioctl to obtain version failed");
+ return {};
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
- close(fd);
- return base::Error() << "Binder driver protocol(" << vers
- << ") does not match user space protocol("
- << BINDER_CURRENT_PROTOCOL_VERSION
- << ")! ioctl() return value: " << result;
+ ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! "
+ "ioctl() return value: %d",
+ vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
+ return {};
}
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
- result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
+ result = ioctl(fd.get(), BINDER_SET_MAX_THREADS, &maxThreads);
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
- result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
+ result = ioctl(fd.get(), BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
if (result == -1) {
ALOGE_IF(ProcessState::isDriverFeatureEnabled(
ProcessState::DriverFeature::ONEWAY_SPAM_DETECTION),
@@ -561,28 +563,27 @@
mThreadPoolStarted(false),
mThreadPoolSeq(1),
mCallRestriction(CallRestriction::NONE) {
- base::Result<int> opened = open_driver(driver);
+ base::unique_fd opened = open_driver(driver);
if (opened.ok()) {
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
- opened.value(), 0);
+ opened.get(), 0);
if (mVMStart == MAP_FAILED) {
- close(opened.value());
// *sigh*
- opened = base::Error()
- << "Using " << driver << " failed: unable to mmap transaction memory.";
+ ALOGE("Using %s failed: unable to mmap transaction memory.", driver);
+ opened.reset();
mDriverName.clear();
}
}
#ifdef __ANDROID__
- LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating: %s",
- driver, opened.error().message().c_str());
+ LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating.",
+ driver);
#endif
if (opened.ok()) {
- mDriverFD = opened.value();
+ mDriverFD = opened.release();
}
}
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 3246706..f7b5a55 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -15,13 +15,14 @@
*/
#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/scopeguard.h>
#include <android-base/unique_fd.h>
+#include <binder/Functional.h>
#include <binder/RecordedTransaction.h>
+#include <inttypes.h>
#include <sys/mman.h>
#include <algorithm>
+using namespace android::binder::impl;
using android::Parcel;
using android::base::borrowed_fd;
using android::base::unique_fd;
@@ -126,18 +127,17 @@
t.mData.mInterfaceName = std::string(String8(interfaceName).c_str());
if (interfaceName.size() != t.mData.mInterfaceName.size()) {
- LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
- "utf-8.";
+ ALOGE("Interface Name is not valid. Contains characters that aren't single byte utf-8.");
return std::nullopt;
}
if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) {
- LOG(ERROR) << "Failed to set sent parcel data.";
+ ALOGE("Failed to set sent parcel data.");
return std::nullopt;
}
if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) {
- LOG(ERROR) << "Failed to set reply parcel data.";
+ ALOGE("Failed to set reply parcel data.");
return std::nullopt;
}
@@ -167,38 +167,37 @@
const long pageSize = sysconf(_SC_PAGE_SIZE);
struct stat fileStat;
if (fstat(fd.get(), &fileStat) != 0) {
- LOG(ERROR) << "Unable to get file information";
+ ALOGE("Unable to get file information");
return std::nullopt;
}
off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
if (fdCurrentPosition == -1) {
- LOG(ERROR) << "Invalid offset in file descriptor.";
+ ALOGE("Invalid offset in file descriptor.");
return std::nullopt;
}
do {
if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
- LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
+ ALOGE("Not enough file remains to contain expected chunk descriptor");
return std::nullopt;
}
if (!android::base::ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) {
- LOG(ERROR) << "Failed to read ChunkDescriptor from fd " << fd.get() << ". "
- << strerror(errno);
+ ALOGE("Failed to read ChunkDescriptor from fd %d. %s", fd.get(), strerror(errno));
return std::nullopt;
}
transaction_checksum_t checksum = *reinterpret_cast<transaction_checksum_t*>(&chunk);
fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
if (fdCurrentPosition == -1) {
- LOG(ERROR) << "Invalid offset in file descriptor.";
+ ALOGE("Invalid offset in file descriptor.");
return std::nullopt;
}
off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
if (chunk.dataSize > kMaxChunkDataSize) {
- LOG(ERROR) << "Chunk data exceeds maximum size.";
+ ALOGE("Chunk data exceeds maximum size.");
return std::nullopt;
}
@@ -206,19 +205,19 @@
chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
- LOG(ERROR) << "Chunk payload exceeds remaining file size.";
+ ALOGE("Chunk payload exceeds remaining file size.");
return std::nullopt;
}
if (PADDING8(chunkPayloadSize) != 0) {
- LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
+ ALOGE("Invalid chunk size, not aligned %zu", chunkPayloadSize);
return std::nullopt;
}
size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset;
void* mappedMemory =
mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart);
- auto mmap_guard = android::base::make_scope_guard(
+ auto mmap_guard = make_scope_guard(
[mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); });
transaction_checksum_t* payloadMap =
@@ -227,8 +226,7 @@
sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
// page-alignment
if (payloadMap == MAP_FAILED) {
- LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " "
- << strerror(errno);
+ ALOGE("Memory mapping failed for fd %d: %d %s", fd.get(), errno, strerror(errno));
return std::nullopt;
}
for (size_t checksumIndex = 0;
@@ -236,21 +234,21 @@
checksum ^= payloadMap[checksumIndex];
}
if (checksum != 0) {
- LOG(ERROR) << "Checksum failed.";
+ ALOGE("Checksum failed.");
return std::nullopt;
}
fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
if (fdCurrentPosition == -1) {
- LOG(ERROR) << "Invalid offset in file descriptor.";
+ ALOGE("Invalid offset in file descriptor.");
return std::nullopt;
}
switch (chunk.chunkType) {
case HEADER_CHUNK: {
if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) {
- LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected "
- << sizeof(TransactionHeader) << ".";
+ ALOGE("Header Chunk indicated size %" PRIu32 "; Expected %zu.", chunk.dataSize,
+ sizeof(TransactionHeader));
return std::nullopt;
}
t.mData.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap);
@@ -264,7 +262,7 @@
case DATA_PARCEL_CHUNK: {
if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap),
chunk.dataSize) != android::NO_ERROR) {
- LOG(ERROR) << "Failed to set sent parcel data.";
+ ALOGE("Failed to set sent parcel data.");
return std::nullopt;
}
break;
@@ -272,7 +270,7 @@
case REPLY_PARCEL_CHUNK: {
if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap),
chunk.dataSize) != android::NO_ERROR) {
- LOG(ERROR) << "Failed to set reply parcel data.";
+ ALOGE("Failed to set reply parcel data.");
return std::nullopt;
}
break;
@@ -280,7 +278,7 @@
case END_CHUNK:
break;
default:
- LOG(INFO) << "Unrecognized chunk.";
+ ALOGI("Unrecognized chunk.");
break;
}
} while (chunk.chunkType != END_CHUNK);
@@ -291,7 +289,7 @@
android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType,
size_t byteCount, const uint8_t* data) const {
if (byteCount > kMaxChunkDataSize) {
- LOG(ERROR) << "Chunk data exceeds maximum size";
+ ALOGE("Chunk data exceeds maximum size");
return BAD_VALUE;
}
ChunkDescriptor descriptor = {.chunkType = chunkType,
@@ -320,7 +318,7 @@
// Write buffer to file
if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) {
- LOG(ERROR) << "Failed to write chunk fd " << fd.get();
+ ALOGE("Failed to write chunk fd %d", fd.get());
return UNKNOWN_ERROR;
}
return NO_ERROR;
@@ -330,26 +328,26 @@
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 07ab093..1ba20b3 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -25,12 +25,11 @@
#include <thread>
#include <vector>
-#include <android-base/scopeguard.h>
+#include <binder/Functional.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <binder/RpcTransportRaw.h>
#include <log/log.h>
-#include <utils/Compat.h>
#include "BuildFlags.h"
#include "FdTrigger.h"
@@ -45,7 +44,7 @@
constexpr size_t kSessionIdBytes = 32;
-using base::ScopeGuard;
+using namespace android::binder::impl;
using base::unique_fd;
RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {}
@@ -231,10 +230,7 @@
}
unique_fd fd(std::move(std::get<unique_fd>(fds.back())));
- if (auto res = binder::os::setNonBlocking(fd); !res.ok()) {
- ALOGE("Failed setNonBlocking: %s", res.error().message().c_str());
- return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
- }
+ if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res;
*out = RpcTransportFd(std::move(fd));
return OK;
@@ -458,11 +454,12 @@
LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(),
"Must establish connection on owned thread");
thisThread = std::move(threadId->second);
- ScopeGuard detachGuard = [&]() {
+ auto detachGuardLambda = [&]() {
thisThread.detach();
_l.unlock();
server->mShutdownCv.notify_all();
};
+ auto detachGuard = make_scope_guard(std::ref(detachGuardLambda));
server->mConnectingThreads.erase(threadId);
if (status != OK || server->mShutdownTrigger->isTriggered()) {
@@ -548,7 +545,7 @@
return;
}
- detachGuard.Disable();
+ detachGuard.release();
session->preJoinThreadOwnership(std::move(thisThread));
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index fa8f2b5..c895b21 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -26,14 +26,12 @@
#include <string_view>
-#include <android-base/macros.h>
-#include <android-base/scopeguard.h>
#include <binder/BpBinder.h>
+#include <binder/Functional.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <binder/RpcTransportRaw.h>
#include <binder/Stability.h>
-#include <utils/Compat.h>
#include <utils/String8.h>
#include "BuildFlags.h"
@@ -52,6 +50,7 @@
namespace android {
+using namespace android::binder::impl;
using base::unique_fd;
RpcSession::RpcSession(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {
@@ -194,10 +193,7 @@
fd = request();
if (!fd.ok()) return BAD_VALUE;
}
- if (auto res = binder::os::setNonBlocking(fd); !res.ok()) {
- ALOGE("setupPreconnectedClient: %s", res.error().message().c_str());
- return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
- }
+ if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res;
RpcTransportFd transportFd(std::move(fd));
status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming);
@@ -412,7 +408,9 @@
}
private:
- DISALLOW_COPY_AND_ASSIGN(JavaThreadAttacher);
+ JavaThreadAttacher(const JavaThreadAttacher&) = delete;
+ void operator=(const JavaThreadAttacher&) = delete;
+
bool mAttached = false;
static JavaVM* getJavaVM() {
@@ -497,7 +495,7 @@
if (auto status = initShutdownTrigger(); status != OK) return status;
auto oldProtocolVersion = mProtocolVersion;
- auto cleanup = base::ScopeGuard([&] {
+ auto cleanup = make_scope_guard([&] {
// if any threads are started, shut them down
(void)shutdownAndWait(true);
@@ -577,7 +575,7 @@
if (status_t status = connectAndInit(mId, true /*incoming*/); status != OK) return status;
}
- cleanup.Disable();
+ cleanup.release();
return OK;
}
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 5046253..008e5d2 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,10 +18,8 @@
#include "RpcState.h"
-#include <android-base/macros.h>
-#include <android-base/scopeguard.h>
-#include <android-base/stringprintf.h>
#include <binder/BpBinder.h>
+#include <binder/Functional.h>
#include <binder/IPCThreadState.h>
#include <binder/RpcServer.h>
@@ -30,6 +28,7 @@
#include "Utils.h"
#include <random>
+#include <sstream>
#include <inttypes.h>
@@ -39,7 +38,7 @@
namespace android {
-using base::StringPrintf;
+using namespace android::binder::impl;
#if RPC_FLAKE_PRONE
void rpcMaybeWaitToFlake() {
@@ -329,8 +328,10 @@
desc = "(not promotable)";
}
- return StringPrintf("node{%p times sent: %zu times recd: %zu type: %s}",
- this->binder.unsafe_get(), this->timesSent, this->timesRecd, desc);
+ std::stringstream ss;
+ ss << "node{" << intptr_t(this->binder.unsafe_get()) << " times sent: " << this->timesSent
+ << " times recd: " << this->timesRecd << " type: " << desc << "}";
+ return ss.str();
}
RpcState::CommandData::CommandData(size_t size) : mSize(size) {
@@ -357,7 +358,7 @@
status_t RpcState::rpcSend(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
const char* what, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
for (int i = 0; i < niovs; i++) {
LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s",
@@ -412,10 +413,8 @@
return false;
}
#else
- // TODO(b/305983144)
- // don't restrict on other platforms, though experimental should
- // only really be used for testing, we don't have a good way to see
- // what is shipping outside of Android
+ ALOGE("Cannot use experimental RPC binder protocol outside of Android.");
+ return false;
#endif
} else if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT) {
ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol "
@@ -602,25 +601,24 @@
{const_cast<uint8_t*>(data.data()), data.dataSize()},
objectTableSpan.toIovec(),
};
- if (status_t status = rpcSend(
- connection, session, "transaction", iovs, arraysize(iovs),
- [&] {
- if (waitUs > kWaitLogUs) {
- ALOGE("Cannot send command, trying to process pending refcounts. Waiting "
- "%zuus. Too many oneway calls?",
- waitUs);
- }
+ auto altPoll = [&] {
+ if (waitUs > kWaitLogUs) {
+ ALOGE("Cannot send command, trying to process pending refcounts. Waiting "
+ "%zuus. Too many oneway calls?",
+ waitUs);
+ }
- if (waitUs > 0) {
- usleep(waitUs);
- waitUs = std::min(kWaitMaxUs, waitUs * 2);
- } else {
- waitUs = 1;
- }
+ if (waitUs > 0) {
+ usleep(waitUs);
+ waitUs = std::min(kWaitMaxUs, waitUs * 2);
+ } else {
+ waitUs = 1;
+ }
- return drainCommands(connection, session, CommandType::CONTROL_ONLY);
- },
- rpcFields->mFds.get());
+ return drainCommands(connection, session, CommandType::CONTROL_ONLY);
+ };
+ if (status_t status = rpcSend(connection, session, "transaction", iovs, countof(iovs),
+ std::ref(altPoll), rpcFields->mFds.get());
status != OK) {
// rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate
// errors here, then we may need to undo the binder-sent counts for the transaction as
@@ -692,7 +690,7 @@
{&rpcReply, rpcReplyWireSize},
{data.data(), data.size()},
};
- if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr);
+ if (status_t status = rpcRec(connection, session, "reply body", iovs, countof(iovs), nullptr);
status != OK)
return status;
@@ -762,7 +760,7 @@
.bodySize = sizeof(RpcDecStrong),
};
iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}};
- return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt);
+ return rpcSend(connection, session, "dec ref", iovs, countof(iovs), std::nullopt);
}
status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
@@ -811,11 +809,11 @@
origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
}
- base::ScopeGuard guardUnguard = [&]() {
+ auto guardUnguard = make_scope_guard([&]() {
if (kernelBinderState != nullptr) {
kernelBinderState->restoreGetCallingSpGuard(origGuard);
}
- };
+ });
#endif // BINDER_WITH_KERNEL_IPC
switch (command.command) {
@@ -1145,7 +1143,7 @@
{const_cast<uint8_t*>(reply.data()), reply.dataSize()},
objectTableSpan.toIovec(),
};
- return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt,
+ return rpcSend(connection, session, "reply", iovs, countof(iovs), std::nullopt,
rpcFields->mFds.get());
}
@@ -1220,10 +1218,11 @@
uint32_t protocolVersion = session->getProtocolVersion().value();
if (protocolVersion < RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE &&
!rpcFields->mObjectPositions.empty()) {
- *errorMsg = StringPrintf("Parcel has attached objects but the session's protocol version "
- "(%" PRIu32 ") is too old, must be at least %" PRIu32,
- protocolVersion,
- RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE);
+ std::stringstream ss;
+ ss << "Parcel has attached objects but the session's protocol version (" << protocolVersion
+ << ") is too old, must be at least "
+ << RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE;
+ *errorMsg = ss.str();
return BAD_VALUE;
}
@@ -1236,9 +1235,10 @@
case RpcSession::FileDescriptorTransportMode::UNIX: {
constexpr size_t kMaxFdsPerMsg = 253;
if (rpcFields->mFds->size() > kMaxFdsPerMsg) {
- *errorMsg = StringPrintf("Too many file descriptors in Parcel for unix "
- "domain socket: %zu (max is %zu)",
- rpcFields->mFds->size(), kMaxFdsPerMsg);
+ std::stringstream ss;
+ ss << "Too many file descriptors in Parcel for unix domain socket: "
+ << rpcFields->mFds->size() << " (max is " << kMaxFdsPerMsg << ")";
+ *errorMsg = ss.str();
return BAD_VALUE;
}
break;
@@ -1249,9 +1249,10 @@
// available on Android
constexpr size_t kMaxFdsPerMsg = 8;
if (rpcFields->mFds->size() > kMaxFdsPerMsg) {
- *errorMsg = StringPrintf("Too many file descriptors in Parcel for Trusty "
- "IPC connection: %zu (max is %zu)",
- rpcFields->mFds->size(), kMaxFdsPerMsg);
+ std::stringstream ss;
+ ss << "Too many file descriptors in Parcel for Trusty IPC connection: "
+ << rpcFields->mFds->size() << " (max is " << kMaxFdsPerMsg << ")";
+ *errorMsg = ss.str();
return BAD_VALUE;
}
break;
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 1fe71a5..2a954e6 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -16,6 +16,7 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <binder/Functional.h>
#include <binder/IBinder.h>
#include <binder/Parcel.h>
#include <binder/RpcSession.h>
@@ -190,7 +191,7 @@
[[nodiscard]] status_t rpcSend(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
const char* what, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds =
nullptr);
[[nodiscard]] status_t rpcRec(
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index c089811..ffa3151 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -29,6 +29,8 @@
namespace android {
+using namespace android::binder::impl;
+
// RpcTransport with TLS disabled.
class RpcTransportRaw : public RpcTransport {
public:
@@ -54,7 +56,7 @@
status_t interruptableWriteFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
override {
bool sentFds = false;
@@ -70,7 +72,7 @@
status_t interruptableReadFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
auto recv = [&](iovec* iovs, int niovs) -> ssize_t {
return binder::os::receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds);
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 0c81d83..188ba3b 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -26,8 +26,7 @@
#include "RpcState.h"
#include "RpcTransportUtils.h"
-using android::base::Error;
-using android::base::Result;
+using namespace android::binder::impl;
namespace android {
@@ -75,7 +74,7 @@
status_t interruptableWriteFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
override {
auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
@@ -93,7 +92,7 @@
status_t interruptableReadFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/)
override {
auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index efb09e9..fef4be4 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -29,6 +29,8 @@
#include "RpcState.h"
#include "Utils.h"
+#include <sstream>
+
#define SHOULD_LOG_TLS_DETAIL false
#if SHOULD_LOG_TLS_DETAIL
@@ -38,6 +40,9 @@
#endif
namespace android {
+
+using namespace android::binder::impl;
+
namespace {
// Implement BIO for socket that ignores SIGPIPE.
@@ -181,10 +186,9 @@
// |sslError| should be from Ssl::getError().
// If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
// return error. Also return error if |fdTrigger| is triggered before or during poll().
- status_t pollForSslError(
- const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger,
- const char* fnString, int additionalEvent,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+ status_t pollForSslError(const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger,
+ const char* fnString, int additionalEvent,
+ const std::optional<SmallFunction<status_t()>>& altPoll) {
switch (sslError) {
case SSL_ERROR_WANT_READ:
return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
@@ -200,7 +204,7 @@
status_t handlePoll(int event, const android::RpcTransportFd& fd, FdTrigger* fdTrigger,
const char* fnString,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+ const std::optional<SmallFunction<status_t()>>& altPoll) {
status_t ret;
if (altPoll) {
ret = (*altPoll)();
@@ -284,12 +288,12 @@
status_t pollRead(void) override;
status_t interruptableWriteFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
override;
status_t interruptableReadFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override;
bool isWaiting() override { return mSocket.isInPollingState(); };
@@ -320,7 +324,7 @@
status_t RpcTransportTls::interruptableWriteFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
(void)ancillaryFds;
@@ -366,7 +370,7 @@
status_t RpcTransportTls::interruptableReadFully(
FdTrigger* fdTrigger, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
(void)ancillaryFds;
diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h
index 32f0db8..a0e502e 100644
--- a/libs/binder/RpcTransportUtils.h
+++ b/libs/binder/RpcTransportUtils.h
@@ -27,7 +27,7 @@
status_t interruptableReadOrWrite(
const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs,
SendOrReceive sendOrReceiveFun, const char* funName, int16_t event,
- const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+ const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll) {
MAYBE_WAIT_IN_FLAKE_MODE;
if (niovs < 0) {
diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp
index 3b53b05..2f2fe7d 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 @@
auto request = [=] {
int tipcFd = tipc_connect(device, port);
if (tipcFd < 0) {
- LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd;
+ ALOGE("Failed to connect to Trusty service. Error code: %d", tipcFd);
return unique_fd();
}
return unique_fd(tipcFd);
};
if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
- LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str();
+ ALOGE("Failed to set up Trusty client. Error: %s", statusToString(status).c_str());
return nullptr;
}
return session;
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 2b67f03..9482e3e 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -56,16 +56,16 @@
[[nodiscard]] const std::optional<unsigned int>& hostPort() const { return mPort; }
private:
- DISALLOW_COPY_AND_ASSIGN(AdbForwarder);
+ AdbForwarder(const AdbForwarder&) = delete;
+ void operator=(const AdbForwarder&) = delete;
explicit AdbForwarder(unsigned int port) : mPort(port) {}
std::optional<unsigned int> mPort;
};
std::optional<AdbForwarder> AdbForwarder::forward(unsigned int devicePort) {
auto result =
execute({"adb", "forward", "tcp:0", "tcp:" + std::to_string(devicePort)}, nullptr);
- if (!result.ok()) {
- ALOGE("Unable to run `adb forward tcp:0 tcp:%d`: %s", devicePort,
- result.error().message().c_str());
+ if (!result.has_value()) {
+ ALOGE("Unable to run `adb forward tcp:0 tcp:%d`", devicePort);
return std::nullopt;
}
// Must end with exit code 0 (`has_value() && value() == 0`)
@@ -94,9 +94,8 @@
if (!mPort.has_value()) return;
auto result = execute({"adb", "forward", "--remove", "tcp:" + std::to_string(*mPort)}, nullptr);
- if (!result.ok()) {
- ALOGE("Unable to run `adb forward --remove tcp:%d`: %s", *mPort,
- result.error().message().c_str());
+ if (!result.has_value()) {
+ ALOGE("Unable to run `adb forward --remove tcp:%d`", *mPort);
return;
}
// Must end with exit code 0 (`has_value() && value() == 0`)
@@ -130,8 +129,7 @@
serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end());
auto result = execute(std::move(serviceDispatcherArgs), &CommandResult::stdoutEndsWithNewLine);
- if (!result.ok()) {
- ALOGE("%s", result.error().message().c_str());
+ if (!result.has_value()) {
return nullptr;
}
diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h
index c5310da..941ba3a 100644
--- a/libs/binder/ServiceManagerHost.h
+++ b/libs/binder/ServiceManagerHost.h
@@ -16,7 +16,6 @@
#pragma once
-#include <android-base/macros.h>
#include <android/os/IServiceManager.h>
namespace android {
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
index 47fd17d..d9a96af 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 @@
}
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 dd632c0..eec09eb 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#pragma once
+
#include <stddef.h>
#include <sys/uio.h>
#include <cstdint>
@@ -22,6 +24,30 @@
#include <log/log.h>
#include <utils/Errors.h>
+#define PLOGE(fmt, ...) \
+ do { \
+ auto savedErrno = errno; \
+ ALOGE(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \
+ } while (0)
+#define PLOGF(fmt, ...) \
+ do { \
+ auto savedErrno = errno; \
+ LOG_ALWAYS_FATAL(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \
+ } while (0)
+
+/* TEMP_FAILURE_RETRY is not available on macOS and Trusty. */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ __typeof__(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+ })
+#endif
+
#define TEST_AND_RETURN(value, expr) \
do { \
if (!(expr)) { \
@@ -32,6 +58,17 @@
namespace android {
+/**
+ * Get the size of a statically initialized array.
+ *
+ * \param N the array to get the size of.
+ * \return the size of the array.
+ */
+template <typename T, size_t N>
+constexpr size_t countof(T (&)[N]) {
+ return N;
+}
+
// avoid optimizations
void zeroMemory(uint8_t* data, size_t size);
diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp
index 52b8f69..3db038f 100644
--- a/libs/binder/UtilsHost.cpp
+++ b/libs/binder/UtilsHost.cpp
@@ -25,6 +25,8 @@
#include <log/log.h>
+#include "Utils.h"
+
namespace android {
CommandResult::~CommandResult() {
@@ -72,8 +74,8 @@
return ss.str();
}
-android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec,
- const std::function<bool(const CommandResult&)>& end) {
+std::optional<CommandResult> execute(std::vector<std::string> argStringVec,
+ const std::function<bool(const CommandResult&)>& end) {
// turn vector<string> into null-terminated char* vector.
std::vector<char*> argv;
argv.reserve(argStringVec.size() + 1);
@@ -82,14 +84,21 @@
CommandResult ret;
android::base::unique_fd outWrite;
- if (!android::base::Pipe(&ret.outPipe, &outWrite))
- return android::base::ErrnoError() << "pipe() for outPipe";
+ if (!android::base::Pipe(&ret.outPipe, &outWrite)) {
+ PLOGE("pipe() for outPipe");
+ return {};
+ }
android::base::unique_fd errWrite;
- if (!android::base::Pipe(&ret.errPipe, &errWrite))
- return android::base::ErrnoError() << "pipe() for errPipe";
+ if (!android::base::Pipe(&ret.errPipe, &errWrite)) {
+ PLOGE("pipe() for errPipe");
+ return {};
+ }
int pid = fork();
- if (pid == -1) return android::base::ErrnoError() << "fork()";
+ if (pid == -1) {
+ PLOGE("fork()");
+ return {};
+ }
if (pid == 0) {
// child
ret.outPipe.reset();
@@ -140,12 +149,19 @@
*errPollFd = {.fd = ret.errPipe.get(), .events = POLLIN};
}
int pollRet = poll(fds, nfds, 1000 /* ms timeout */);
- if (pollRet == -1) return android::base::ErrnoError() << "poll()";
+ if (pollRet == -1) {
+ PLOGE("poll()");
+ return {};
+ }
- if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr))
- return android::base::ErrnoError() << "read(stdout)";
- if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr))
- return android::base::ErrnoError() << "read(stderr)";
+ if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) {
+ PLOGE("read(stdout)");
+ return {};
+ }
+ if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) {
+ PLOGE("read(stderr)");
+ return {};
+ }
if (end && end(ret)) return ret;
}
@@ -154,7 +170,10 @@
while (ret.pid.has_value()) {
int status;
auto exitPid = waitpid(pid, &status, 0);
- if (exitPid == -1) return android::base::ErrnoError() << "waitpid(" << pid << ")";
+ if (exitPid == -1) {
+ PLOGE("waitpid(%d)", pid);
+ return {};
+ }
if (exitPid == pid) {
if (WIFEXITED(status)) {
ret.pid = std::nullopt;
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index 98ac4e0..5de0980 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -23,8 +23,8 @@
#include <vector>
#include <android-base/macros.h>
-#include <android-base/result.h>
#include <android-base/unique_fd.h>
+#include <utils/Errors.h>
/**
* Log a lot more information about host-device binder communication, when debugging issues.
@@ -67,7 +67,8 @@
}
private:
- DISALLOW_COPY_AND_ASSIGN(CommandResult);
+ CommandResult(const CommandResult&) = delete;
+ void operator=(const CommandResult&) = delete;
};
std::ostream& operator<<(std::ostream& os, const CommandResult& res);
@@ -94,6 +95,6 @@
//
// If the parent process has encountered any errors for system calls, return ExecuteError with
// the proper errno set.
-android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec,
- const std::function<bool(const CommandResult&)>& end);
+std::optional<CommandResult> execute(std::vector<std::string> argStringVec,
+ const std::function<bool(const CommandResult&)>& end);
} // namespace android
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 28fb9f1..d78ea0d 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -18,7 +18,7 @@
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
-#include <utils/Mutex.h>
+#include <binder/RpcThreads.h>
#include <map>
#include <optional>
@@ -193,7 +193,7 @@
void reportOneDeath(const Obituary& obit);
bool isDescriptorCached() const;
- mutable Mutex mLock;
+ mutable RpcMutex mLock;
volatile int32_t mAlive;
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
@@ -201,7 +201,7 @@
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
- static Mutex sTrackingLock;
+ static RpcMutex sTrackingLock;
static std::unordered_map<int32_t,uint32_t> sTrackingMap;
static int sNumTrackedUids;
static std::atomic_bool sCountByUidEnabled;
diff --git a/libs/binder/include/binder/Functional.h b/libs/binder/include/binder/Functional.h
new file mode 100644
index 0000000..08e3b21
--- /dev/null
+++ b/libs/binder/include/binder/Functional.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+
+namespace android::binder::impl {
+
+template <typename F>
+constexpr void assert_small_callable() {
+ // While this buffer (std::function::__func::__buf_) is an implementation detail generally not
+ // accessible to users, it's a good bet to assume its size to be around 3 pointers.
+ constexpr size_t kFunctionBufferSize = 3 * sizeof(void*);
+
+ static_assert(sizeof(F) <= kFunctionBufferSize,
+ "Supplied callable is larger than std::function optimization buffer. "
+ "Try using std::ref, but make sure lambda lives long enough to be called.");
+}
+
+template <typename F>
+std::unique_ptr<void, std::function<void(void*)>> make_scope_guard(F&& f) {
+ assert_small_callable<decltype(std::bind(f))>();
+ return {reinterpret_cast<void*>(true), std::bind(f)};
+}
+
+template <typename T>
+class SmallFunction : public std::function<T> {
+public:
+ template <typename F>
+ SmallFunction(F&& f) : std::function<T>(f) {
+ assert_small_callable<F>();
+ }
+};
+
+} // namespace android::binder::impl
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 98d12bb..6961abc 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -33,7 +33,6 @@
#include <utils/RefBase.h>
#include <utils/String16.h>
#include <utils/Vector.h>
-#include <utils/Flattenable.h>
#include <binder/IInterface.h>
#include <binder/Parcelable.h>
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9dc370b..3672702 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -17,13 +17,13 @@
#pragma once
#include <binder/IBinder.h>
-#include <utils/KeyedVector.h>
-#include <utils/Mutex.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <pthread.h>
+#include <mutex>
+
// ---------------------------------------------------------------------------
namespace android {
@@ -178,7 +178,7 @@
// Time when thread pool was emptied
int64_t mStarvationStartTimeMs;
- mutable Mutex mLock; // protects everything below.
+ mutable std::mutex mLock; // protects everything below.
Vector<handle_entry> mHandleToObject;
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index cb64603..e3805ac 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -15,7 +15,6 @@
*/
#pragma once
-#include <android-base/threads.h>
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/RpcThreads.h>
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 6db9ad9..115a173 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -25,10 +25,10 @@
#include <variant>
#include <vector>
-#include <android-base/function_ref.h>
#include <android-base/unique_fd.h>
#include <utils/Errors.h>
+#include <binder/Functional.h>
#include <binder/RpcCertificateFormat.h>
#include <binder/RpcThreads.h>
@@ -85,13 +85,13 @@
* error - interrupted (failure or trigger)
*/
[[nodiscard]] virtual status_t interruptableWriteFully(
- FdTrigger *fdTrigger, iovec *iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>> &altPoll,
- const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) = 0;
[[nodiscard]] virtual status_t interruptableReadFully(
- FdTrigger *fdTrigger, iovec *iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>> &altPoll,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) = 0;
/**
* Check whether any threads are blocked while polling the transport
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index f51cd9b..118409e 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 @@
}
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 @@
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 @@
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 @@
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_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 @@
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 @@
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 @@
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_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 @@
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 58ed418..47b9f58 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -60,6 +60,7 @@
"libbinder.cpp",
"parcel.cpp",
"parcel_jni.cpp",
+ "persistable_bundle.cpp",
"process.cpp",
"stability.cpp",
"status.cpp",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 47da296..bf7a0ba 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 @@
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 @@
};
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
}
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::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 @@
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 @@
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_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_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_decStrong(AIBinder* binder) {
if (binder == nullptr) {
- LOG(ERROR) << __func__ << ": on null binder";
+ ALOGE("%s: on null binder", __func__);
return;
}
@@ -604,7 +598,7 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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_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 0000000..f178027
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/binder_parcel.h>
+#include <android/persistable_bundle.h>
+#include <sys/cdefs.h>
+
+#include <set>
+#include <sstream>
+
+namespace aidl::android::os {
+
+/**
+ * Wrapper class that enables interop with AIDL NDK generation
+ * Takes ownership of the APersistableBundle* given to it in reset() and will automatically
+ * destroy it in the destructor, similar to a smart pointer container
+ */
+class PersistableBundle {
+ public:
+ PersistableBundle() noexcept : mPBundle(APersistableBundle_new()) {}
+ // takes ownership of the APersistableBundle*
+ PersistableBundle(APersistableBundle* _Nonnull bundle) noexcept : mPBundle(bundle) {}
+ // takes ownership of the APersistableBundle*
+ PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {}
+ // duplicates, does not take ownership of the APersistableBundle*
+ PersistableBundle(const PersistableBundle& other) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ mPBundle = APersistableBundle_dup(other.mPBundle);
+ }
+ }
+ // duplicates, does not take ownership of the APersistableBundle*
+ PersistableBundle& operator=(const PersistableBundle& other) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ mPBundle = APersistableBundle_dup(other.mPBundle);
+ }
+ return *this;
+ }
+
+ ~PersistableBundle() { reset(); }
+
+ binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
+ reset();
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_readFromParcel(parcel, &mPBundle);
+ } else {
+ return STATUS_FAILED_TRANSACTION;
+ }
+ }
+
+ binder_status_t writeToParcel(AParcel* _Nonnull parcel) const {
+ if (!mPBundle) {
+ return STATUS_BAD_VALUE;
+ }
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_writeToParcel(mPBundle, parcel);
+ } else {
+ return STATUS_FAILED_TRANSACTION;
+ }
+ }
+
+ /**
+ * Destroys any currently owned APersistableBundle* and takes ownership of the given
+ * APersistableBundle*
+ *
+ * @param pBundle The APersistableBundle to take ownership of
+ */
+ void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept {
+ if (mPBundle) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_delete(mPBundle);
+ }
+ mPBundle = nullptr;
+ }
+ mPBundle = pBundle;
+ }
+
+ /**
+ * Check the actual contents of the bundle for equality. This is typically
+ * what should be used to check for equality.
+ */
+ bool deepEquals(const PersistableBundle& rhs) const {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_isEqual(get(), rhs.get());
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * NOTE: This does NOT check the contents of the PersistableBundle. This is
+ * implemented for ordering. Use deepEquals() to check for equality between
+ * two different PersistableBundle objects.
+ */
+ inline bool operator==(const PersistableBundle& rhs) const { return get() == rhs.get(); }
+ inline bool operator!=(const PersistableBundle& rhs) const { return get() != rhs.get(); }
+
+ inline bool operator<(const PersistableBundle& rhs) const { return get() < rhs.get(); }
+ inline bool operator>(const PersistableBundle& rhs) const { return get() > rhs.get(); }
+ inline bool operator>=(const PersistableBundle& rhs) const { return !(*this < rhs); }
+ inline bool operator<=(const PersistableBundle& rhs) const { return !(*this > rhs); }
+
+ PersistableBundle& operator=(PersistableBundle&& other) noexcept {
+ reset(other.release());
+ return *this;
+ }
+
+ /**
+ * Stops managing any contained APersistableBundle*, returning it to the caller. Ownership
+ * is released.
+ * @return APersistableBundle* or null if this was empty
+ */
+ [[nodiscard]] APersistableBundle* _Nullable release() noexcept {
+ APersistableBundle* _Nullable ret = mPBundle;
+ mPBundle = nullptr;
+ return ret;
+ }
+
+ inline std::string toString() const {
+ if (!mPBundle) {
+ return "<PersistableBundle: null>";
+ } else if (__builtin_available(android __ANDROID_API_V__, *)) {
+ std::ostringstream os;
+ os << "<PersistableBundle: ";
+ os << "size: " << std::to_string(APersistableBundle_size(mPBundle));
+ os << " >";
+ return os.str();
+ }
+ return "<PersistableBundle (unknown)>";
+ }
+
+ int32_t size() const {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_size(mPBundle);
+ } else {
+ return 0;
+ }
+ }
+
+ int32_t erase(const std::string& key) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_erase(mPBundle, key.c_str());
+ } else {
+ return 0;
+ }
+ }
+
+ void putBoolean(const std::string& key, bool val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putBoolean(mPBundle, key.c_str(), val);
+ }
+ }
+
+ void putInt(const std::string& key, int32_t val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putInt(mPBundle, key.c_str(), val);
+ }
+ }
+
+ void putLong(const std::string& key, int64_t val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putLong(mPBundle, key.c_str(), val);
+ }
+ }
+
+ void putDouble(const std::string& key, double val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putDouble(mPBundle, key.c_str(), val);
+ }
+ }
+
+ void putString(const std::string& key, const std::string& val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putString(mPBundle, key.c_str(), val.c_str());
+ }
+ }
+
+ void putBooleanVector(const std::string& key, const std::vector<bool>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ // std::vector<bool> has no ::data().
+ int32_t num = vec.size();
+ if (num > 0) {
+ bool* newVec = (bool*)malloc(num * sizeof(bool));
+ if (newVec) {
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ APersistableBundle_putBooleanVector(mPBundle, key.c_str(), newVec, num);
+ free(newVec);
+ }
+ }
+ }
+ }
+
+ void putIntVector(const std::string& key, const std::vector<int32_t>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t num = vec.size();
+ if (num > 0) {
+ APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num);
+ }
+ }
+ }
+ void putLongVector(const std::string& key, const std::vector<int64_t>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t num = vec.size();
+ if (num > 0) {
+ APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num);
+ }
+ }
+ }
+ void putDoubleVector(const std::string& key, const std::vector<double>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t num = vec.size();
+ if (num > 0) {
+ APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num);
+ }
+ }
+ }
+ void putStringVector(const std::string& key, const std::vector<std::string>& vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t num = vec.size();
+ if (num > 0) {
+ char** inVec = (char**)malloc(num * sizeof(char*));
+ if (inVec) {
+ for (int32_t i = 0; i < num; i++) {
+ inVec[i] = strdup(vec[i].c_str());
+ }
+ APersistableBundle_putStringVector(mPBundle, key.c_str(), inVec, num);
+ free(inVec);
+ }
+ }
+ }
+ }
+ void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle);
+ }
+ }
+
+ bool getBoolean(const std::string& key, bool* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_getBoolean(mPBundle, key.c_str(), val);
+ } else {
+ return false;
+ }
+ }
+
+ bool getInt(const std::string& key, int32_t* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_getInt(mPBundle, key.c_str(), val);
+ } else {
+ return false;
+ }
+ }
+
+ bool getLong(const std::string& key, int64_t* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_getLong(mPBundle, key.c_str(), val);
+ } else {
+ return false;
+ }
+ }
+
+ bool getDouble(const std::string& key, double* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return APersistableBundle_getDouble(mPBundle, key.c_str(), val);
+ } else {
+ return false;
+ }
+ }
+
+ static char* _Nullable stringAllocator(int32_t bufferSizeBytes, void* _Nullable) {
+ return (char*)malloc(bufferSizeBytes);
+ }
+
+ bool getString(const std::string& key, std::string* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ char* outString = nullptr;
+ bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString,
+ &stringAllocator, nullptr);
+ if (ret && outString) {
+ *val = std::string(outString);
+ }
+ return ret;
+ } else {
+ return false;
+ }
+ }
+
+ template <typename T>
+ bool getVecInternal(int32_t (*_Nonnull getVec)(const APersistableBundle* _Nonnull,
+ const char* _Nonnull, T* _Nullable, int32_t),
+ const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ std::vector<T>* _Nonnull vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ int32_t bytes = 0;
+ // call first with nullptr to get required size in bytes
+ bytes = getVec(pBundle, key, nullptr, 0);
+ if (bytes > 0) {
+ T* newVec = (T*)malloc(bytes);
+ if (newVec) {
+ bytes = getVec(pBundle, key, newVec, bytes);
+ int32_t elements = bytes / sizeof(T);
+ vec->clear();
+ for (int32_t i = 0; i < elements; i++) {
+ vec->push_back(newVec[i]);
+ }
+ free(newVec);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) {
+ return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
+ vec);
+ }
+ bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) {
+ return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
+ vec);
+ }
+ bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) {
+ return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
+ vec);
+ }
+ bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) {
+ return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, key.c_str(),
+ vec);
+ }
+
+ // Takes ownership of and frees the char** and its elements.
+ // Creates a new set or vector based on the array of char*.
+ template <typename T>
+ T moveStringsInternal(char* _Nullable* _Nonnull strings, int32_t bufferSizeBytes) {
+ if (strings && bufferSizeBytes > 0) {
+ int32_t num = bufferSizeBytes / sizeof(char*);
+ T ret;
+ for (int32_t i = 0; i < num; i++) {
+ ret.insert(ret.end(), std::string(strings[i]));
+ free(strings[i]);
+ }
+ free(strings);
+ return ret;
+ }
+ return T();
+ }
+
+ bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) {
+ int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
+ &stringAllocator, nullptr);
+ if (bytes > 0) {
+ char** strings = (char**)malloc(bytes);
+ if (strings) {
+ bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, bytes,
+ &stringAllocator, nullptr);
+ *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ APersistableBundle* bundle = nullptr;
+ bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle);
+ if (ret) {
+ *val = PersistableBundle(bundle);
+ }
+ return ret;
+ } else {
+ return false;
+ }
+ }
+
+ std::set<std::string> getKeys(
+ int32_t (*_Nonnull getTypedKeys)(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable),
+ const APersistableBundle* _Nonnull pBundle) {
+ // call first with nullptr to get required size in bytes
+ int32_t bytes = getTypedKeys(pBundle, nullptr, 0, &stringAllocator, nullptr);
+ if (bytes > 0) {
+ char** keys = (char**)malloc(bytes);
+ if (keys) {
+ bytes = getTypedKeys(pBundle, keys, bytes, &stringAllocator, nullptr);
+ return moveStringsInternal<std::set<std::string>>(keys, bytes);
+ }
+ }
+ return {};
+ }
+
+ std::set<std::string> getBooleanKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getBooleanKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getIntKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getIntKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getLongKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getLongKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getDoubleKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getDoubleKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getStringKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getStringKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getBooleanVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getIntVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getLongVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getDoubleVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getStringVectorKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getPersistableBundleKeys() {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle);
+ } else {
+ return {};
+ }
+ }
+ std::set<std::string> getMonKeys() {
+ // :P
+ return {"c(o,o)b", "c(o,o)b"};
+ }
+
+ private:
+ inline APersistableBundle* _Nullable get() const { return mPBundle; }
+ APersistableBundle* _Nullable mPBundle = nullptr;
+};
+
+} // namespace aidl::android::os
diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
new file mode 100644
index 0000000..eff8104
--- /dev/null
+++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
@@ -0,0 +1,905 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/binder_parcel.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/*
+ * A mapping from string keys to values of various types.
+ * See frameworks/base/core/java/android/os/PersistableBundle.java
+ * for the Java type than can be used in SDK APIs.
+ * APersistableBundle exists to be used in AIDL interfaces and seamlessly
+ * interact with framework services.
+ * frameworks/native/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+ * contains the AIDL type used in the ndk backend of AIDL interfaces.
+ */
+struct APersistableBundle;
+typedef struct APersistableBundle APersistableBundle;
+
+/**
+ * This is a user supplied allocator that allocates a buffer for the
+ * APersistableBundle APIs to fill in with a string.
+ *
+ * \param the required size in bytes for the allocated buffer
+ * \param void* _Nullable context if needed by the callback
+ *
+ * \return allocated buffer of sizeBytes. Null if allocation failed.
+ */
+typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t sizeBytes,
+ void* _Nullable context);
+
+/**
+ * Create a new APersistableBundle.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \return Pointer to a new APersistableBundle
+ */
+APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Create a new APersistableBundle based off an existing APersistableBundle.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to duplicate
+ *
+ * \return Pointer to a new APersistableBundle
+ */
+APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _Nonnull pBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Delete an APersistableBundle. This must always be called when finished using
+ * the object.
+ *
+ * \param bundle to delete
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_delete(APersistableBundle* _Nonnull pBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Check for equality of APersistableBundles.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param lhs bundle to compare agains the other param
+ * \param rhs bundle to compare agains the other param
+ *
+ * \return true when equal, false when not
+ */
+bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs,
+ const APersistableBundle* _Nonnull rhs)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Read an APersistableBundle from an AParcel.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param parcel to read from
+ * \param outPBundle bundle to write to
+ *
+ * \return STATUS_OK on success
+ * STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an
+ * issue deserializing (eg, corrupted parcel)
+ * STATUS_BAD_TYPE if the parcel's current data position is not that of
+ * an APersistableBundle type
+ * STATUS_NO_MEMORY if an allocation fails
+ */
+binder_status_t APersistableBundle_readFromParcel(
+ const AParcel* _Nonnull parcel, APersistableBundle* _Nullable* _Nonnull outPBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Write an APersistableBundle to an AParcel.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param pBundle bundle to write to the parcel
+ * \param parcel to write to
+ *
+ * \return STATUS_OK on success.
+ * STATUS_BAD_VALUE if either pBundle or parcel is null, or if the
+ * APersistableBundle*
+ * fails to serialize (eg, internally corrupted)
+ * STATUS_NO_MEMORY if the parcel runs out of space to store the pBundle & is
+ * unable to allocate more
+ * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs
+ */
+binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonnull pBundle,
+ AParcel* _Nonnull parcel)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get the size of an APersistableBundle. This is the number of mappings in the
+ * object.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to get the size of (number of mappings)
+ *
+ * \return number of mappings in the object
+ */
+int32_t APersistableBundle_size(APersistableBundle* _Nonnull pBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Erase any entries added with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping to erase
+ *
+ * \return number of entries erased. Either 0 or 1.
+ */
+int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a boolean associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ bool val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an int32_t associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ int32_t val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an int64_t associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ int64_t val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a double associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ double val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a string associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a boolean vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, const bool* _Nonnull vec,
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an int32_t vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ const int32_t* _Nonnull vec, int32_t num)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an int64_t vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, const int64_t* _Nonnull vec,
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a double vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, const double* _Nonnull vec,
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put a string vector associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ * \param size in number of elements in the vector
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key,
+ const char* _Nullable const* _Nullable vec, int32_t num)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Put an APersistableBundle associated with the provided key.
+ * New values with the same key will overwrite existing values.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param value to put for the mapping
+ *
+ * Available since API level __ANDROID_API_V__.
+ */
+void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key,
+ const APersistableBundle* _Nonnull val)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a boolean associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, bool* _Nonnull val)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an int32_t associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
+ int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an int64_t associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, int64_t* _Nonnull val)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a double associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, double* _Nonnull val)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a string associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to write the value to
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of string associated with the provided key on success
+ * 0 if no string exists for the provided key
+ * -1 if the provided allocator fails and returns false
+ */
+int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, char* _Nullable* _Nonnull val,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a boolean vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ */
+int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, bool* _Nullable buffer,
+ int32_t bufferSizeBytes)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an int32_t vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ */
+int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, int32_t* _Nullable buffer,
+ int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an int64_t vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ */
+int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, int64_t* _Nullable buffer,
+ int32_t bufferSizeBytes)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a double vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ */
+int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key, double* _Nullable buffer,
+ int32_t bufferSizeBytes)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get a string vector associated with the provided key and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes of stored vector.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the stored vector in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key,
+ char* _Nullable* _Nullable buffer,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get an APersistableBundle* associated with the provided key.
+ *
+ * Available since API level __ANDROID_API_V__.
+ *
+ * \param bundle to operate on
+ * \param key for the mapping
+ * \param nonnull pointer to an APersistableBundle pointer to write to point to
+ * a new copy of the stored APersistableBundle. The caller takes ownership of
+ * the new APersistableBundle and must be deleted with
+ * APersistableBundle_delete.
+ *
+ * \return true if a value exists for the provided key
+ */
+bool APersistableBundle_getPersistableBundle(const APersistableBundle* _Nonnull pBundle,
+ const char* _Nonnull key,
+ APersistableBundle* _Nullable* _Nonnull outBundle)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnull pBundle,
+ char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Get all of the keys associated with this specific type and place it in the
+ * provided pre-allocated buffer from the user. The user must provide an
+ * APersistableBundle_stringAllocator for the individual strings to be
+ * allocated.
+ *
+ * This function returns the size in bytes required to fit the fill list of keys.
+ * The supplied buffer will be filled in based on the smaller of the suplied
+ * bufferSizeBytes or the actual size of the stored data.
+ * If the buffer is null or if the supplied bufferSizeBytes is smaller than the
+ * actual stored data, then not all of the stored data will be returned.
+ *
+ * Users can call this function with null buffer and 0 bufferSizeBytes to get
+ * the required size of the buffer to use on a subsequent call.
+ *
+ * \param bundle to operate on
+ * \param nonnull pointer to a pre-allocated buffer to write the values to
+ * \param size of the pre-allocated buffer
+ * \param function pointer to the string dup allocator
+ *
+ * \return size of the buffer of keys in bytes. This is the required size of the
+ * pre-allocated user supplied buffer if all of the stored contents are desired.
+ * 0 if no string vector exists for the provided key
+ * -1 if the user supplied APersistableBundle_stringAllocator returns
+ * false
+ */
+int32_t APersistableBundle_getPersistableBundleKeys(
+ const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys,
+ int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
+
+__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 1c5f79f..0843a8e 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -161,6 +161,51 @@
AServiceManager_addServiceWithFlags; # systemapi llndk
};
+LIBBINDER_NDK35 { # introduced=VanillaIceCream
+ global:
+ APersistableBundle_readFromParcel;
+ APersistableBundle_writeToParcel;
+ APersistableBundle_new;
+ APersistableBundle_dup;
+ APersistableBundle_delete;
+ APersistableBundle_isEqual;
+ APersistableBundle_size;
+ APersistableBundle_erase;
+ APersistableBundle_putBoolean;
+ APersistableBundle_putInt;
+ APersistableBundle_putLong;
+ APersistableBundle_putDouble;
+ APersistableBundle_putString;
+ APersistableBundle_putBooleanVector;
+ APersistableBundle_putIntVector;
+ APersistableBundle_putLongVector;
+ APersistableBundle_putDoubleVector;
+ APersistableBundle_putStringVector;
+ APersistableBundle_putPersistableBundle;
+ APersistableBundle_getBoolean;
+ APersistableBundle_getInt;
+ APersistableBundle_getLong;
+ APersistableBundle_getDouble;
+ APersistableBundle_getString;
+ APersistableBundle_getBooleanVector;
+ APersistableBundle_getIntVector;
+ APersistableBundle_getLongVector;
+ APersistableBundle_getDoubleVector;
+ APersistableBundle_getStringVector;
+ APersistableBundle_getPersistableBundle;
+ APersistableBundle_getBooleanKeys;
+ APersistableBundle_getIntKeys;
+ APersistableBundle_getLongKeys;
+ APersistableBundle_getDoubleKeys;
+ APersistableBundle_getStringKeys;
+ APersistableBundle_getBooleanVectorKeys;
+ APersistableBundle_getIntVectorKeys;
+ APersistableBundle_getLongVectorKeys;
+ APersistableBundle_getDoubleVectorKeys;
+ APersistableBundle_getStringVectorKeys;
+ APersistableBundle_getPersistableBundleKeys;
+};
+
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 037aa2e..c15bcf9 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -14,20 +14,19 @@
* 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 <binder/Parcel.h>
+#include <binder/ParcelFileDescriptor.h>
+#include <inttypes.h>
+#include <utils/Unicode.h>
#include <limits>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <binder/Parcel.h>
-#include <binder/ParcelFileDescriptor.h>
-#include <utils/Unicode.h>
+#include "ibinder_internal.h"
+#include "parcel_internal.h"
+#include "status_internal.h"
using ::android::IBinder;
using ::android::Parcel;
@@ -52,11 +51,11 @@
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_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 @@
}
if (length < 0) {
- LOG(WARNING) << __func__ << ": Negative string length: " << length;
+ ALOGW("Negative string length: %" PRIi32, length);
return STATUS_BAD_VALUE;
}
@@ -342,7 +341,7 @@
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 @@
}
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 @@
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 0000000..404611c
--- /dev/null
+++ b/libs/binder/ndk/persistable_bundle.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/binder_libbinder.h>
+#include <android/persistable_bundle.h>
+#include <binder/PersistableBundle.h>
+#include <log/log.h>
+#include <persistable_bundle_internal.h>
+#include <string.h>
+
+#include <set>
+
+__BEGIN_DECLS
+
+struct APersistableBundle {
+ APersistableBundle(const APersistableBundle& pBundle) : mPBundle(pBundle.mPBundle) {}
+ APersistableBundle(const android::os::PersistableBundle& pBundle) : mPBundle(pBundle) {}
+ APersistableBundle() = default;
+ android::os::PersistableBundle mPBundle;
+};
+
+APersistableBundle* _Nullable APersistableBundle_new() {
+ return new (std::nothrow) APersistableBundle();
+}
+
+APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* pBundle) {
+ if (pBundle) {
+ return new APersistableBundle(*pBundle);
+ } else {
+ return new APersistableBundle();
+ }
+}
+
+void APersistableBundle_delete(APersistableBundle* pBundle) {
+ free(pBundle);
+}
+
+bool APersistableBundle_isEqual(const APersistableBundle* lhs, const APersistableBundle* rhs) {
+ if (lhs && rhs) {
+ return lhs->mPBundle == rhs->mPBundle;
+ } else if (lhs == rhs) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+binder_status_t APersistableBundle_readFromParcel(const AParcel* parcel,
+ APersistableBundle* _Nullable* outPBundle) {
+ if (!parcel || !outPBundle) return STATUS_BAD_VALUE;
+ APersistableBundle* newPBundle = APersistableBundle_new();
+ if (newPBundle == nullptr) return STATUS_NO_MEMORY;
+ binder_status_t status =
+ newPBundle->mPBundle.readFromParcel(AParcel_viewPlatformParcel(parcel));
+ if (status == STATUS_OK) {
+ *outPBundle = newPBundle;
+ }
+ return status;
+}
+
+binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* pBundle,
+ AParcel* parcel) {
+ if (!parcel || !pBundle) return STATUS_BAD_VALUE;
+ return pBundle->mPBundle.writeToParcel(AParcel_viewPlatformParcel(parcel));
+}
+
+int32_t APersistableBundle_size(APersistableBundle* pBundle) {
+ size_t size = pBundle->mPBundle.size();
+ LOG_ALWAYS_FATAL_IF(size > INT32_MAX,
+ "The APersistableBundle has gotten too large! There will be an overflow in "
+ "the reported size.");
+ return pBundle->mPBundle.size();
+}
+int32_t APersistableBundle_erase(APersistableBundle* pBundle, const char* key) {
+ return pBundle->mPBundle.erase(android::String16(key));
+}
+void APersistableBundle_putBoolean(APersistableBundle* pBundle, const char* key, bool val) {
+ pBundle->mPBundle.putBoolean(android::String16(key), val);
+}
+void APersistableBundle_putInt(APersistableBundle* pBundle, const char* key, int32_t val) {
+ pBundle->mPBundle.putInt(android::String16(key), val);
+}
+void APersistableBundle_putLong(APersistableBundle* pBundle, const char* key, int64_t val) {
+ pBundle->mPBundle.putLong(android::String16(key), val);
+}
+void APersistableBundle_putDouble(APersistableBundle* pBundle, const char* key, double val) {
+ pBundle->mPBundle.putDouble(android::String16(key), val);
+}
+void APersistableBundle_putString(APersistableBundle* pBundle, const char* key, const char* val) {
+ pBundle->mPBundle.putString(android::String16(key), android::String16(val));
+}
+void APersistableBundle_putBooleanVector(APersistableBundle* pBundle, const char* key,
+ const bool* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<bool> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ pBundle->mPBundle.putBooleanVector(android::String16(key), newVec);
+}
+void APersistableBundle_putIntVector(APersistableBundle* pBundle, const char* key,
+ const int32_t* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<int32_t> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ pBundle->mPBundle.putIntVector(android::String16(key), newVec);
+}
+void APersistableBundle_putLongVector(APersistableBundle* pBundle, const char* key,
+ const int64_t* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<int64_t> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ pBundle->mPBundle.putLongVector(android::String16(key), newVec);
+}
+void APersistableBundle_putDoubleVector(APersistableBundle* pBundle, const char* key,
+ const double* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<double> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = vec[i];
+ }
+ pBundle->mPBundle.putDoubleVector(android::String16(key), newVec);
+}
+void APersistableBundle_putStringVector(APersistableBundle* pBundle, const char* key,
+ const char* const* vec, int32_t num) {
+ LOG_ALWAYS_FATAL_IF(num < 0, "Negative number of elements is invalid.");
+ std::vector<android::String16> newVec(num);
+ for (int32_t i = 0; i < num; i++) {
+ newVec[i] = android::String16(vec[i]);
+ }
+ pBundle->mPBundle.putStringVector(android::String16(key), newVec);
+}
+void APersistableBundle_putPersistableBundle(APersistableBundle* pBundle, const char* key,
+ const APersistableBundle* val) {
+ pBundle->mPBundle.putPersistableBundle(android::String16(key), val->mPBundle);
+}
+bool APersistableBundle_getBoolean(const APersistableBundle* pBundle, const char* key, bool* val) {
+ return pBundle->mPBundle.getBoolean(android::String16(key), val);
+}
+bool APersistableBundle_getInt(const APersistableBundle* pBundle, const char* key, int32_t* val) {
+ return pBundle->mPBundle.getInt(android::String16(key), val);
+}
+bool APersistableBundle_getLong(const APersistableBundle* pBundle, const char* key, int64_t* val) {
+ return pBundle->mPBundle.getLong(android::String16(key), val);
+}
+bool APersistableBundle_getDouble(const APersistableBundle* pBundle, const char* key, double* val) {
+ return pBundle->mPBundle.getDouble(android::String16(key), val);
+}
+int32_t APersistableBundle_getString(const APersistableBundle* pBundle, const char* key, char** val,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ android::String16 outVal;
+ bool ret = pBundle->mPBundle.getString(android::String16(key), &outVal);
+ if (ret) {
+ android::String8 tmp8(outVal);
+ *val = stringAllocator(tmp8.bytes() + 1, context);
+ if (*val) {
+ strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1);
+ return tmp8.bytes();
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+int32_t APersistableBundle_getBooleanVector(const APersistableBundle* pBundle, const char* key,
+ bool* buffer, int32_t bufferSizeBytes) {
+ std::vector<bool> newVec;
+ pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec);
+ return getVecInternal<bool>(newVec, buffer, bufferSizeBytes);
+}
+int32_t APersistableBundle_getIntVector(const APersistableBundle* pBundle, const char* key,
+ int32_t* buffer, int32_t bufferSizeBytes) {
+ std::vector<int32_t> newVec;
+ pBundle->mPBundle.getIntVector(android::String16(key), &newVec);
+ return getVecInternal<int32_t>(newVec, buffer, bufferSizeBytes);
+}
+int32_t APersistableBundle_getLongVector(const APersistableBundle* pBundle, const char* key,
+ int64_t* buffer, int32_t bufferSizeBytes) {
+ std::vector<int64_t> newVec;
+ pBundle->mPBundle.getLongVector(android::String16(key), &newVec);
+ return getVecInternal<int64_t>(newVec, buffer, bufferSizeBytes);
+}
+int32_t APersistableBundle_getDoubleVector(const APersistableBundle* pBundle, const char* key,
+ double* buffer, int32_t bufferSizeBytes) {
+ std::vector<double> newVec;
+ pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec);
+ return getVecInternal<double>(newVec, buffer, bufferSizeBytes);
+}
+int32_t APersistableBundle_getStringVector(const APersistableBundle* pBundle, const char* key,
+ char** vec, int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::vector<android::String16> newVec;
+ pBundle->mPBundle.getStringVector(android::String16(key), &newVec);
+ return getStringsInternal<std::vector<android::String16>>(newVec, vec, bufferSizeBytes,
+ stringAllocator, context);
+}
+bool APersistableBundle_getPersistableBundle(const APersistableBundle* pBundle, const char* key,
+ APersistableBundle** outBundle) {
+ APersistableBundle* bundle = APersistableBundle_new();
+ bool ret = pBundle->mPBundle.getPersistableBundle(android::String16(key), &bundle->mPBundle);
+ if (ret) {
+ *outBundle = bundle;
+ return true;
+ }
+ return false;
+}
+int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getBooleanKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getIntKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getIntKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getLongKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getLongKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getDoubleKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getStringKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getStringKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getBooleanVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getIntVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getLongVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getDoubleVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* pBundle, char** outKeys,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getStringVectorKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+int32_t APersistableBundle_getPersistableBundleKeys(
+ const APersistableBundle* pBundle, char** outKeys, int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator, void* context) {
+ std::set<android::String16> ret = pBundle->mPBundle.getPersistableBundleKeys();
+ return getStringsInternal<std::set<android::String16>>(ret, outKeys, bufferSizeBytes,
+ stringAllocator, context);
+}
+
+__END_DECLS
diff --git a/libs/binder/ndk/persistable_bundle_internal.h b/libs/binder/ndk/persistable_bundle_internal.h
new file mode 100644
index 0000000..279c66f
--- /dev/null
+++ b/libs/binder/ndk/persistable_bundle_internal.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/persistable_bundle.h>
+#include <log/log.h>
+#include <utils/String8.h>
+
+// take a vector and put the contents into a buffer.
+// return the size of the contents.
+// This may not put all of the contents into the buffer if the buffer is not
+// large enough.
+template <typename T>
+int32_t getVecInternal(const std::vector<T>& inVec, T* _Nullable buffer, int32_t bufferSizeBytes) {
+ LOG_ALWAYS_FATAL_IF(inVec.size() > INT32_MAX,
+ "The size of the APersistableBundle has gotten too large!");
+ LOG_ALWAYS_FATAL_IF(
+ bufferSizeBytes < 0,
+ "The buffer size in bytes can not be larger than INT32_MAX and can not be negative.");
+ int32_t num = inVec.size();
+ int32_t numAvailable = bufferSizeBytes / sizeof(T);
+ int32_t numFill = numAvailable < num ? numAvailable : num;
+
+ if (numFill > 0 && buffer) {
+ for (int32_t i = 0; i < numFill; i++) {
+ buffer[i] = inVec[i];
+ }
+ }
+ return num * sizeof(T);
+}
+
+// take a vector or a set of String16 and put the contents into a char** buffer.
+// return the size of the contents.
+// This may not put all of the contents into the buffer if the buffer is not
+// large enough.
+// The strings are duped with a user supplied callback
+template <typename T>
+int32_t getStringsInternal(const T& strings, char* _Nullable* _Nullable buffer,
+ int32_t bufferSizeBytes,
+ APersistableBundle_stringAllocator stringAllocator,
+ void* _Nullable context) {
+ LOG_ALWAYS_FATAL_IF(strings.size() > INT32_MAX,
+ "The size of the APersistableBundle has gotten too large!");
+ LOG_ALWAYS_FATAL_IF(
+ bufferSizeBytes < 0,
+ "The buffer size in bytes can not be larger than INT32_MAX and can not be negative.");
+ int32_t num = strings.size();
+ int32_t numAvailable = bufferSizeBytes / sizeof(char*);
+ int32_t numFill = numAvailable < num ? numAvailable : num;
+ if (!stringAllocator) {
+ return -1;
+ }
+
+ if (numFill > 0 && buffer) {
+ int32_t i = 0;
+ for (const auto& val : strings) {
+ android::String8 tmp8 = android::String8(val);
+ buffer[i] = stringAllocator(tmp8.bytes() + 1, context);
+ if (buffer[i] == nullptr) {
+ return -1;
+ }
+ strncpy(buffer[i], tmp8.c_str(), tmp8.bytes() + 1);
+ i++;
+ if (i > numFill - 1) {
+ // buffer is too small to keep going or this is the end of the
+ // set
+ break;
+ }
+ }
+ }
+ return num * sizeof(char*);
+}
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index 0fea57b..0072ac3 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 2977786..3bfdc59 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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/stability.cpp b/libs/binder/ndk/stability.cpp
index 7eafb9c..73eb863 100644
--- a/libs/binder/ndk/stability.cpp
+++ b/libs/binder/ndk/stability.cpp
@@ -27,6 +27,10 @@
#error libbinder_ndk should only be built in a system context
#endif
+#ifdef __ANDROID_VENDOR__
+#error libbinder_ndk should only be built in a system context
+#endif
+
#ifdef __ANDROID_NDK__
#error libbinder_ndk should only be built in a system context
#endif
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 8ed91a5..3aac3c0 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 @@
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 @@
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 a08cb7a..6d122c5 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -21,12 +21,13 @@
use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
use crate::sys;
+use downcast_rs::{impl_downcast, DowncastSync};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fmt;
-use std::fs::File;
+use std::io::Write;
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::raw::c_char;
@@ -51,7 +52,7 @@
/// interfaces) must implement this trait.
///
/// This is equivalent `IInterface` in C++.
-pub trait Interface: Send + Sync {
+pub trait Interface: Send + Sync + DowncastSync {
/// Convert this binder object into a generic [`SpIBinder`] reference.
fn as_binder(&self) -> SpIBinder {
panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
@@ -61,11 +62,13 @@
///
/// This handler is a no-op by default and should be implemented for each
/// Binder service struct that wishes to respond to dump transactions.
- fn dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> {
+ fn dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> {
Ok(())
}
}
+impl_downcast!(sync Interface);
+
/// Implemented by sync interfaces to specify what the associated async interface is.
/// Generic to handle the fact that async interfaces are generic over a thread pool.
///
@@ -143,7 +146,7 @@
/// When using the AIDL backend, users need only implement the high-level AIDL-defined
/// interface. The AIDL compiler then generates a container struct that wraps
/// the user-defined service and implements `Remotable`.
-pub trait Remotable: Send + Sync {
+pub trait Remotable: Send + Sync + 'static {
/// The Binder interface descriptor string.
///
/// This string is a unique identifier for a Binder interface, and should be
@@ -162,7 +165,7 @@
/// Handle a request to invoke the dump transaction on this
/// object.
- fn on_dump(&self, file: &File, args: &[&CStr]) -> Result<()>;
+ fn on_dump(&self, file: &mut dyn Write, args: &[&CStr]) -> Result<()>;
/// Retrieve the class of this remote object.
///
@@ -893,6 +896,23 @@
$crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
+
+ /// Tries to downcast the interface to another type.
+ /// When receiving this object from a binder call, make sure that the object received is
+ /// a binder native object and that is of the right type for the Downcast:
+ ///
+ /// let binder = received_object.as_binder();
+ /// if !binder.is_remote() {
+ /// let binder_native: Binder<BnFoo> = binder.try_into()?;
+ /// let original_object = binder_native.downcast_binder::<MyFoo>();
+ /// // Check that returned type is not None before using it
+ /// }
+ ///
+ /// Handle the error cases instead of just calling `unwrap` or `expect` to prevent a
+ /// malicious caller to mount a Denial of Service attack.
+ pub fn downcast_binder<T: $interface>(&self) -> Option<&T> {
+ self.0.as_any().downcast_ref::<T>()
+ }
}
impl $crate::binder_impl::Remotable for $native {
@@ -914,8 +934,8 @@
}
}
- fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
- self.0.dump(file, args)
+ fn on_dump(&self, writer: &mut dyn std::io::Write, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
+ self.0.dump(writer, args)
}
fn get_class() -> $crate::binder_impl::InterfaceClass {
@@ -1004,7 +1024,7 @@
$(
// Async interface trait implementations.
- impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::FromIBinder for dyn $async_interface<P> {
fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> {
use $crate::binder_impl::AssociateClass;
@@ -1030,27 +1050,27 @@
}
}
- impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
let binder = $crate::Interface::as_binder(self);
parcel.write(&binder)
}
}
- impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&this.map($crate::Interface::as_binder))
}
}
- impl<P: $crate::BinderAsyncPool> std::fmt::Debug for dyn $async_interface<P> + '_ {
+ impl<P: $crate::BinderAsyncPool + 'static> std::fmt::Debug for dyn $async_interface<P> + '_ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(stringify!($async_interface))
}
}
/// Convert a &dyn $async_interface to Strong<dyn $async_interface>
- impl<P: $crate::BinderAsyncPool> std::borrow::ToOwned for dyn $async_interface<P> {
+ impl<P: $crate::BinderAsyncPool + 'static> std::borrow::ToOwned for dyn $async_interface<P> {
type Owned = $crate::Strong<dyn $async_interface<P>>;
fn to_owned(&self) -> Self::Owned {
self.as_binder().into_interface()
@@ -1058,11 +1078,11 @@
}
}
- impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
type Target = dyn $async_interface<P>;
}
- impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
+ impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
type Target = dyn $interface;
}
)?
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index b248f5e..b250012 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -25,6 +25,7 @@
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 @@
}
// 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 @@
// 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 @@
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.rs b/libs/binder/rust/src/parcel.rs
index 3c615ed..f9f135d 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -54,6 +54,10 @@
/// Safety: This type guarantees that it owns the AParcel and that all access to
/// the AParcel happens through the Parcel, so it is ok to send across threads.
+///
+/// It would not be okay to implement Sync, because that would allow you to call
+/// the reading methods from several threads in parallel, which would be a data
+/// race on the cursor position inside the AParcel.
unsafe impl Send for Parcel {}
/// Container for a message (data and object references) that can be sent
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 5c688fa..6afe5ab 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -22,29 +22,28 @@
use crate::error::{status_result, Result, StatusCode};
use crate::sys;
-use std::fs::File;
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
/// Rust version of the Java class android.os.ParcelFileDescriptor
#[derive(Debug)]
-pub struct ParcelFileDescriptor(File);
+pub struct ParcelFileDescriptor(OwnedFd);
impl ParcelFileDescriptor {
/// Create a new `ParcelFileDescriptor`
- pub fn new(file: File) -> Self {
- Self(file)
+ pub fn new<F: Into<OwnedFd>>(fd: F) -> Self {
+ Self(fd.into())
}
}
-impl AsRef<File> for ParcelFileDescriptor {
- fn as_ref(&self) -> &File {
+impl AsRef<OwnedFd> for ParcelFileDescriptor {
+ fn as_ref(&self) -> &OwnedFd {
&self.0
}
}
-impl From<ParcelFileDescriptor> for File {
- fn from(file: ParcelFileDescriptor) -> File {
- file.0
+impl From<ParcelFileDescriptor> for OwnedFd {
+ fn from(fd: ParcelFileDescriptor) -> OwnedFd {
+ fd.0
}
}
@@ -120,7 +119,7 @@
// Safety: At this point, we know that the file descriptor was
// not -1, so must be a valid, owned file descriptor which we
// can safely turn into a `File`.
- let file = unsafe { File::from_raw_fd(fd) };
+ let file = unsafe { OwnedFd::from_raw_fd(fd) };
Ok(Some(ParcelFileDescriptor::new(file)))
}
}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index c049b80..c87fa89 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -26,7 +26,7 @@
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 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 37f182e..fbedfee 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -58,7 +58,7 @@
let wrong_service: Result<binder::Strong<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
binder::get_interface(service_name);
match wrong_service {
- Err(e) if e == StatusCode::BAD_TYPE => {}
+ Err(StatusCode::BAD_TYPE) => {}
Err(e) => {
eprintln!("Trying to use a service via the wrong interface errored with unexpected error {:?}", e);
return e as c_int;
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 5bf9680..f2693dd 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -22,7 +22,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android/debug/BnAdbCallback.h>
#include <android/debug/IAdbManager.h>
#include <android/os/BnServiceManager.h>
@@ -46,7 +45,6 @@
using android::base::LogId;
using android::base::LogSeverity;
using android::base::StdioLogger;
-using android::base::StringPrintf;
using std::string_view_literals::operator""sv;
namespace {
@@ -57,11 +55,12 @@
int Usage(const char* program) {
auto basename = Basename(program);
- auto format = R"(dispatch calls to RPC service.
+ // clang-format off
+ LOG(ERROR) << R"(dispatch calls to RPC service.
Usage:
- %s [-g] [-i <ip_address>] <service_name>
+ )" << basename << R"( [-g] [-i <ip_address>] <service_name>
<service_name>: the service to connect to.
- %s [-g] manager
+ )" << basename << R"( [-g] manager
Runs an RPC-friendly service that redirects calls to servicemanager.
-g: use getService() instead of checkService().
@@ -71,7 +70,7 @@
blocks until killed.
Otherwise, writes error message to stderr and exits with non-zero code.
)";
- LOG(ERROR) << StringPrintf(format, basename.c_str(), basename.c_str());
+ // clang-format on
return EX_USAGE;
}
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index 6712c9c..7e0b594 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -16,6 +16,7 @@
#include <android-base/logging.h>
#include <binder/Binder.h>
+#include <binder/Functional.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
@@ -28,6 +29,8 @@
#include <functional>
#include <vector>
+using namespace android::binder::impl;
+
static android::String8 gEmpty(""); // make sure first allocation from optimization runs
struct DestructionAction {
@@ -172,6 +175,18 @@
a_binder->pingBinder();
}
+TEST(BinderAllocation, MakeScopeGuard) {
+ const auto m = ScopeDisallowMalloc();
+ {
+ auto guard1 = make_scope_guard([] {});
+ guard1.release();
+
+ auto guard2 = make_scope_guard([&guard1, ptr = imaginary_use] {
+ if (ptr == nullptr) guard1.release();
+ });
+ }
+}
+
TEST(BinderAllocation, InterfaceDescriptorTransaction) {
sp<IBinder> a_binder = GetRemoteBinder();
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 0075688..0ae536c 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -75,7 +75,7 @@
public:
void SetUp() override {
auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr);
- ASSERT_THAT(debuggableResult, Ok());
+ ASSERT_TRUE(debuggableResult.has_value());
ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult;
auto debuggableBool = ParseBool(Trim(debuggableResult->stdoutStr));
ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdoutStr);
@@ -84,7 +84,7 @@
}
auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr);
- ASSERT_THAT(lsResult, Ok());
+ ASSERT_TRUE(lsResult.has_value());
if (lsResult->exitCode != 0) {
GTEST_SKIP() << "b/182914638: until feature is fully enabled, skip test on devices "
"without servicedispatcher";
@@ -95,7 +95,7 @@
auto service = execute({"adb", "shell", kServiceBinary, kServiceName, kDescriptor},
&CommandResult::stdoutEndsWithNewLine);
- ASSERT_THAT(service, Ok());
+ ASSERT_TRUE(service.has_value());
ASSERT_EQ(std::nullopt, service->exitCode) << *service;
mService = std::move(*service);
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 341e9ce..f3969f1 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -29,17 +29,17 @@
#include <android-base/properties.h>
#include <android-base/result-gmock.h>
-#include <android-base/result.h>
-#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
+#include <binder/Functional.h>
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
+#include <utils/Flattenable.h>
#include <linux/sched.h>
#include <sys/epoll.h>
@@ -52,6 +52,7 @@
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
using namespace android;
+using namespace android::binder::impl;
using namespace std::string_literals;
using namespace std::chrono_literals;
using android::base::testing::HasValue;
@@ -1325,7 +1326,7 @@
ASSERT_EQ(0, ret);
// Restore the original file limits when the test finishes
- base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); });
+ auto guardUnguard = make_scope_guard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); });
rlimit testNofile = {1024, 1024};
ret = setrlimit(RLIMIT_NOFILE, &testNofile);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 1340ea1..8e42ec2 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -18,7 +18,6 @@
// only used on NDK tests outside of vendor
#include <aidl/IBinderRpcTest.h>
#endif
-#include <android-base/stringprintf.h>
#include <chrono>
#include <cstdlib>
@@ -36,6 +35,7 @@
#include <trusty/tipc.h>
#endif // BINDER_RPC_TO_TRUSTY_TEST
+#include "../Utils.h"
#include "binderRpcTestCommon.h"
#include "binderRpcTestFixture.h"
@@ -59,12 +59,12 @@
static std::string WaitStatusToString(int wstatus) {
if (WIFEXITED(wstatus)) {
- return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
+ return std::format("exit status {}", WEXITSTATUS(wstatus));
}
if (WIFSIGNALED(wstatus)) {
- return base::StringPrintf("term signal %d", WTERMSIG(wstatus));
+ return std::format("term signal {}", WTERMSIG(wstatus));
}
- return base::StringPrintf("unexpected state %d", wstatus);
+ return std::format("unexpected state {}", wstatus);
}
static void debugBacktrace(pid_t pid) {
@@ -87,8 +87,8 @@
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);
@@ -146,8 +146,10 @@
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;
}
@@ -205,14 +207,12 @@
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;
}
@@ -221,8 +221,7 @@
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;
@@ -231,8 +230,7 @@
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);
}
@@ -246,10 +244,12 @@
// 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;
@@ -260,9 +260,9 @@
bool noKernel = GetParam().noKernel;
std::string path = android::base::GetExecutableDirectory();
- auto servicePath = android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(),
- singleThreaded ? "_single_threaded" : "",
- noKernel ? "_no_kernel" : "");
+ auto servicePath =
+ std::format("{}/binder_rpc_test_service{}{}", path,
+ singleThreaded ? "_single_threaded" : "", noKernel ? "_no_kernel" : "");
base::unique_fd bootstrapClientFd, socketFd;
@@ -274,8 +274,7 @@
// 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()");
}
}
@@ -288,8 +287,10 @@
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;
@@ -334,16 +335,16 @@
}
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));
}
}
@@ -356,7 +357,7 @@
? 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);
@@ -408,7 +409,7 @@
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;
@@ -589,12 +590,12 @@
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);
@@ -951,8 +952,8 @@
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) {
@@ -981,7 +982,7 @@
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");
}
@@ -1006,7 +1007,7 @@
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'));
}
@@ -1198,7 +1199,7 @@
{.fd = serverFd.get(), .events = POLLIN, .revents = 0},
{.fd = connectFd.get(), .events = POLLOUT, .revents = 0},
};
- ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+ ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1));
LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno));
if (pfd[0].revents & POLLIN) {
@@ -1359,7 +1360,11 @@
};
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.";
@@ -1387,8 +1392,7 @@
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 "
@@ -1593,12 +1597,10 @@
iovec iov{&buf, sizeof(buf)};
if (binder::os::receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
- int savedErrno = errno;
- LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno);
+ PLOGF("Failed receiveMessage");
}
- if (fds.size() != 1) {
- LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size();
- }
+ 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]));
}
@@ -2083,7 +2085,7 @@
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 eceff35..b2b63e4 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -22,7 +22,6 @@
#include <BnBinderRpcCallback.h>
#include <BnBinderRpcSession.h>
#include <BnBinderRpcTest.h>
-#include <android-base/stringprintf.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
@@ -37,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>
@@ -58,6 +59,7 @@
#include "../BuildFlags.h"
#include "../FdTrigger.h"
#include "../RpcState.h" // for debugging
+#include "format.h"
#include "utils/Errors.h"
namespace android {
@@ -74,8 +76,7 @@
#ifdef __ANDROID__
return base::GetProperty("ro.build.version.codename", "") != "REL";
#else
- // TODO(b/305983144): restrict on other platforms
- return true;
+ return false;
#endif
}
@@ -91,7 +92,7 @@
}
static inline std::string trustyIpcPort(uint32_t serverVersion) {
- return base::StringPrintf("com.android.trusty.binderRpcTestService.V%" PRIu32, serverVersion);
+ return std::format("com.android.trusty.binderRpcTestService.V{}", serverVersion);
}
enum class SocketType {
@@ -157,21 +158,21 @@
#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()));
}
@@ -179,9 +180,10 @@
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;
}
@@ -208,7 +210,7 @@
// 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 7435f30..5025bd6 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -65,7 +65,7 @@
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 @@
};
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 @@
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 @@
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 @@
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 @@
// nothing to save
break;
case AF_VSOCK:
- CHECK_EQ(len, sizeof(sockaddr_vm));
+ LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_vm));
service->port = reinterpret_cast<const sockaddr_vm*>(addr)->svm_port;
break;
case AF_INET:
- CHECK_EQ(len, sizeof(sockaddr_in));
+ LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in));
service->port = ntohs(reinterpret_cast<const sockaddr_in*>(addr)->sin_port);
break;
case AF_INET6:
- CHECK_EQ(len, sizeof(sockaddr_in));
+ LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in));
service->port = ntohs(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port);
break;
default:
diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
index cb632e9..aaca8d0 100644
--- a/libs/binder/tests/binderRpcTestServiceTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
@@ -16,7 +16,6 @@
#define TLOG_TAG "binderRpcTestService"
-#include <android-base/stringprintf.h>
#include <binder/RpcServerTrusty.h>
#include <inttypes.h>
#include <lib/tipc/tipc.h>
@@ -28,7 +27,6 @@
#include "binderRpcTestCommon.h"
using namespace android;
-using android::base::StringPrintf;
using binder::Status;
static int gConnectionCounter = 0;
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index fcb83bd..8acaae6 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -16,7 +16,6 @@
#define LOG_TAG "binderRpcTest"
-#include <android-base/stringprintf.h>
#include <binder/RpcTransportTipcTrusty.h>
#include <trusty-gtest.h>
#include <trusty_ipc.h>
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index 7ec7c99..e59dc82 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -15,7 +15,6 @@
*/
#include <android-base/logging.h>
-#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <binder/Parcel.h>
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 1c13866..cbbbe74 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -28,6 +28,7 @@
#include <gtest/gtest.h>
#pragma clang diagnostic pop
+#include <utils/Flattenable.h>
#include <utils/LightRefBase.h>
#include <utils/NativeHandle.h>
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index 25e286c..6301c74 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -32,7 +32,7 @@
TEST(UtilsHost, ExecuteImmediately) {
auto result = execute({"echo", "foo"}, nullptr);
- ASSERT_THAT(result, Ok());
+ ASSERT_TRUE(result.has_value());
EXPECT_THAT(result->exitCode, Optional(EX_OK));
EXPECT_EQ(result->stdoutStr, "foo\n");
}
@@ -58,7 +58,7 @@
EXPECT_GE(elapsedMs, 1000);
EXPECT_LT(elapsedMs, 2000);
- ASSERT_THAT(result, Ok());
+ ASSERT_TRUE(result.has_value());
EXPECT_EQ(std::nullopt, result->exitCode);
EXPECT_EQ(result->stdoutStr, "foo\n");
}
@@ -83,7 +83,7 @@
EXPECT_GE(elapsedMs, 4000);
EXPECT_LT(elapsedMs, 6000);
- ASSERT_THAT(result, Ok());
+ ASSERT_TRUE(result.has_value());
EXPECT_EQ(std::nullopt, result->exitCode);
EXPECT_EQ(result->stdoutStr, "foo\n");
}
@@ -104,7 +104,7 @@
return false;
});
- ASSERT_THAT(result, Ok());
+ ASSERT_TRUE(result.has_value());
EXPECT_EQ(std::nullopt, result->exitCode);
EXPECT_THAT(result->signal, Optional(SIGKILL));
}
diff --git a/libs/binder/tests/format.h b/libs/binder/tests/format.h
new file mode 100644
index 0000000..b5440a4
--- /dev/null
+++ b/libs/binder/tests/format.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+// TODO(b/302723053): remove this header and replace with <format> once b/175635923 is done
+// ETA for this blocker is 2023-10-27~2023-11-10.
+// Also, remember to remove fmtlib's format.cc from trusty makefiles.
+
+#if __has_include(<format>)
+#include <format>
+#else
+#include <fmt/format.h>
+
+namespace std {
+using fmt::format;
+}
+#endif
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 383795e..fe79f8e 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -32,7 +32,11 @@
host_supported: true,
fuzz_config: {
- cc: ["smoreland@google.com"],
+ cc: [
+ "smoreland@google.com",
+ "waghpawan@google.com",
+ ],
+ use_for_presubmit: true,
},
srcs: [
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 416ffad..ffeca2d 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -25,6 +25,7 @@
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
#include <binder/Status.h>
+#include <utils/Flattenable.h>
#include "../../Utils.h"
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index f0beed2..f367b41 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -66,6 +66,11 @@
},
// write FD
[&]() {
+ // b/296516864 - Limit number of objects written to a parcel.
+ if (p->objectsCount() > 100) {
+ return;
+ }
+
if (options->extraFds.size() > 0 && provider.ConsumeBool()) {
const base::unique_fd& fd = options->extraFds.at(
provider.ConsumeIntegralInRange<size_t>(0,
@@ -82,7 +87,6 @@
CHECK(OK ==
p->writeFileDescriptor(fds.begin()->release(),
true /*takeOwnership*/));
-
options->extraFds.insert(options->extraFds.end(),
std::make_move_iterator(fds.begin() + 1),
std::make_move_iterator(fds.end()));
@@ -90,6 +94,11 @@
},
// write binder
[&]() {
+ // b/296516864 - Limit number of objects written to a parcel.
+ if (p->objectsCount() > 100) {
+ return;
+ }
+
sp<IBinder> binder;
if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
binder = options->extraBinders.at(
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
index e494366..0706182 100644
--- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <android-base/macros.h>
#include <binder/RecordedTransaction.h>
#include <filesystem>
@@ -35,8 +34,8 @@
if (transaction.has_value()) {
intermediateFile = std::tmpfile();
- android::base::unique_fd fdForWriting(fileno(intermediateFile));
- auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+ android::base::unique_fd fdForWriting(dup(fileno(intermediateFile)));
+ auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting);
std::fclose(intermediateFile);
}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
index 33a653e..9289f6a 100644
--- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <android-base/macros.h>
#include <binder/RecordedTransaction.h>
#include <fuzzbinder/random_parcel.h>
#include <filesystem>
@@ -55,7 +54,7 @@
if (transaction.has_value()) {
std::FILE* intermediateFile = std::tmpfile();
android::base::unique_fd fdForWriting(dup(fileno(intermediateFile)));
- auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+ auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting);
std::fclose(intermediateFile);
}
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 43e06e0..0d18b0b 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -26,13 +26,11 @@
#include "../OS.h"
#include "TrustyStatus.h"
-using android::base::Result;
-
namespace android::binder::os {
-Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) {
+status_t setNonBlocking(android::base::borrowed_fd /*fd*/) {
// Trusty IPC syscalls are all non-blocking by default.
- return {};
+ return OK;
}
status_t getRandomBytes(uint8_t* data, size_t size) {
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index 692f82d..6bb45e2 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -29,6 +29,8 @@
namespace android {
+using namespace android::binder::impl;
+
// RpcTransport for Trusty.
class RpcTransportTipcTrusty : public RpcTransport {
public:
@@ -45,7 +47,7 @@
status_t interruptableWriteFully(
FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/,
+ const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
override {
if (niovs < 0) {
@@ -115,7 +117,7 @@
status_t interruptableReadFully(
FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/,
+ const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
if (niovs < 0) {
return BAD_VALUE;
diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json
index 1cefac5..6e20b8a 100644
--- a/libs/binder/trusty/binderRpcTest/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/manifest.json
@@ -2,5 +2,5 @@
"uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b",
"app_name": "binderRpcTest",
"min_heap": 262144,
- "min_stack": 16384
+ "min_stack": 20480
}
diff --git a/libs/binder/trusty/binderRpcTest/rules.mk b/libs/binder/trusty/binderRpcTest/rules.mk
index 975f689..e46ccfb 100644
--- a/libs/binder/trusty/binderRpcTest/rules.mk
+++ b/libs/binder/trusty/binderRpcTest/rules.mk
@@ -21,6 +21,7 @@
MANIFEST := $(LOCAL_DIR)/manifest.json
MODULE_SRCS += \
+ $(FMTLIB_DIR)/src/format.cc \
$(LIBBINDER_TESTS_DIR)/binderRpcUniversalTests.cpp \
$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
$(LIBBINDER_TESTS_DIR)/binderRpcTestTrusty.cpp \
diff --git a/libs/binder/trusty/binderRpcTest/service/rules.mk b/libs/binder/trusty/binderRpcTest/service/rules.mk
index 5d1a51d..50ae3d2 100644
--- a/libs/binder/trusty/binderRpcTest/service/rules.mk
+++ b/libs/binder/trusty/binderRpcTest/service/rules.mk
@@ -21,6 +21,7 @@
MANIFEST := $(LOCAL_DIR)/manifest.json
MODULE_SRCS := \
+ $(FMTLIB_DIR)/src/format.cc \
$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
$(LIBBINDER_TESTS_DIR)/binderRpcTestServiceTrusty.cpp \
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 8924b36..aa476f9 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -17,7 +17,6 @@
#pragma once
#include <android-base/expected.h>
-#include <android-base/macros.h>
#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/RpcServer.h>
@@ -83,7 +82,8 @@
// Both this class and RpcServer have multiple non-copyable fields,
// including mPortAcl below which can't be copied because mUuidPtrs
// holds pointers into it
- DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty);
+ RpcServerTrusty(const RpcServerTrusty&) = delete;
+ void operator=(const RpcServerTrusty&) = delete;
friend sp<RpcServerTrusty>;
explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index 1f05ef7..d2b37aa 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -24,7 +24,6 @@
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 b4243af..0000000
--- a/libs/binder/trusty/logging.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TLOG_TAG "libbinder"
-
-#include "android-base/logging.h"
-
-#include <trusty_log.h>
-#include <iostream>
-#include <string>
-
-#include <android-base/macros.h>
-#include <android-base/strings.h>
-
-namespace android {
-namespace base {
-
-static const char* GetFileBasename(const char* file) {
- const char* last_slash = strrchr(file, '/');
- if (last_slash != nullptr) {
- return last_slash + 1;
- }
- return file;
-}
-
-// This splits the message up line by line, by calling log_function with a pointer to the start of
-// each line and the size up to the newline character. It sends size = -1 for the final line.
-template <typename F, typename... Args>
-static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
- const char* newline;
- while ((newline = strchr(msg, '\n')) != nullptr) {
- log_function(msg, newline - msg, args...);
- msg = newline + 1;
- }
-
- log_function(msg, -1, args...);
-}
-
-void DefaultAborter(const char* abort_message) {
- TLOGC("aborting: %s\n", abort_message);
- abort();
-}
-
-static void TrustyLogLine(const char* msg, int /*length*/, android::base::LogSeverity severity,
- const char* tag) {
- switch (severity) {
- case VERBOSE:
- case DEBUG:
- TLOGD("%s: %s\n", tag, msg);
- break;
- case INFO:
- TLOGI("%s: %s\n", tag, msg);
- break;
- case WARNING:
- TLOGW("%s: %s\n", tag, msg);
- break;
- case ERROR:
- TLOGE("%s: %s\n", tag, msg);
- break;
- case FATAL_WITHOUT_ABORT:
- case FATAL:
- TLOGC("%s: %s\n", tag, msg);
- break;
- }
-}
-
-void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
- const char*, unsigned int, const char* full_message) {
- SplitByLines(full_message, TrustyLogLine, severity, tag);
-}
-
-// This indirection greatly reduces the stack impact of having lots of
-// checks/logging in a function.
-class LogMessageData {
-public:
- LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- int error)
- : file_(GetFileBasename(file)),
- line_number_(line),
- severity_(severity),
- tag_(tag),
- error_(error) {}
-
- const char* GetFile() const { return file_; }
-
- unsigned int GetLineNumber() const { return line_number_; }
-
- LogSeverity GetSeverity() const { return severity_; }
-
- const char* GetTag() const { return tag_; }
-
- int GetError() const { return error_; }
-
- std::ostream& GetBuffer() { return buffer_; }
-
- std::string ToString() const { return buffer_.str(); }
-
-private:
- std::ostringstream buffer_;
- const char* const file_;
- const unsigned int line_number_;
- const LogSeverity severity_;
- const char* const tag_;
- const int error_;
-
- DISALLOW_COPY_AND_ASSIGN(LogMessageData);
-};
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
- const char* tag, int error)
- : LogMessage(file, line, severity, tag, error) {}
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- int error)
- : data_(new LogMessageData(file, line, severity, tag, error)) {}
-
-LogMessage::~LogMessage() {
- // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
- if (!WOULD_LOG(data_->GetSeverity())) {
- return;
- }
-
- // Finish constructing the message.
- if (data_->GetError() != -1) {
- data_->GetBuffer() << ": " << strerror(data_->GetError());
- }
- std::string msg(data_->ToString());
-
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- msg.c_str());
-
- // Abort if necessary.
- if (data_->GetSeverity() == FATAL) {
- DefaultAborter(msg.c_str());
- }
-}
-
-std::ostream& LogMessage::stream() {
- return data_->GetBuffer();
-}
-
-void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
- const char* message) {
- TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message);
-}
-
-bool ShouldLog(LogSeverity /*severity*/, const char* /*tag*/) {
- // This is controlled by Trusty's log level.
- return true;
-}
-
-} // namespace base
-} // namespace android
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 2e56cbd..9cad556 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -24,7 +24,6 @@
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/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
index d4605ea..3fe71ce 100644
--- a/libs/bufferqueueconverter/Android.bp
+++ b/libs/bufferqueueconverter/Android.bp
@@ -34,5 +34,7 @@
"libbase",
"liblog",
],
+ static_libs: ["libguiflags"],
export_include_dirs: ["include"],
+ export_static_lib_headers: ["libguiflags"],
}
diff --git a/libs/bufferstreams/examples/app/Android.bp b/libs/bufferstreams/examples/app/Android.bp
index 0ecf94c..d6305f8 100644
--- a/libs/bufferstreams/examples/app/Android.bp
+++ b/libs/bufferstreams/examples/app/Android.bp
@@ -14,14 +14,33 @@
android_app {
name: "BufferStreamsDemoApp",
- srcs: ["java/**/*.java"],
+ srcs: ["java/**/*.kt"],
sdk_version: "current",
jni_uses_platform_apis: true,
jni_libs: ["libbufferstreamdemoapp"],
use_embedded_native_libs: true,
+ kotlincflags: [
+ "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
+ ],
+
+ resource_dirs: ["res"],
static_libs: [
+ "androidx.activity_activity-compose",
"androidx.appcompat_appcompat",
+ "androidx.compose.foundation_foundation",
+ "androidx.compose.material3_material3",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui",
+ "androidx.compose.ui_ui-graphics",
+ "androidx.compose.ui_ui-tooling-preview",
+ "androidx.core_core-ktx",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.navigation_navigation-common-ktx",
+ "androidx.navigation_navigation-compose",
+ "androidx.navigation_navigation-fragment-ktx",
+ "androidx.navigation_navigation-runtime-ktx",
+ "androidx.navigation_navigation-ui-ktx",
],
}
diff --git a/libs/bufferstreams/examples/app/AndroidManifest.xml b/libs/bufferstreams/examples/app/AndroidManifest.xml
index 872193c..a5e2fa8 100644
--- a/libs/bufferstreams/examples/app/AndroidManifest.xml
+++ b/libs/bufferstreams/examples/app/AndroidManifest.xml
@@ -9,14 +9,15 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
- android:theme="@style/Theme.AppCompat.Light"
+ android:theme="@style/Theme.Jetpack"
tools:targetApi="34">
<activity
android:name=".MainActivity"
- android:exported="true">
+ android:exported="true"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.Jetpack">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt
new file mode 100644
index 0000000..ff3ae5a
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt
@@ -0,0 +1,40 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+
+@Composable
+fun BufferDemosAppBar(
+ currentScreen: BufferDemoScreen,
+ canNavigateBack: Boolean,
+ navigateUp: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ TopAppBar(
+ title = { Text(stringResource(currentScreen.title)) },
+ colors =
+ TopAppBarDefaults.mediumTopAppBarColors(
+ containerColor = MaterialTheme.colorScheme.primaryContainer
+ ),
+ modifier = modifier,
+ navigationIcon = {
+ if (canNavigateBack) {
+ IconButton(onClick = navigateUp) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = stringResource(R.string.back_button)
+ )
+ }
+ }
+ }
+ )
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt
new file mode 100644
index 0000000..a2db934
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt
@@ -0,0 +1,27 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+class BufferStreamJNI {
+ // Used to load the 'bufferstreamsdemoapp' library on application startup.
+ init {
+ System.loadLibrary("bufferstreamdemoapp")
+ }
+
+ /**
+ * A native method that is implemented by the 'bufferstreamsdemoapp' native library, which is
+ * packaged with this application.
+ */
+ external fun stringFromJNI()
+ external fun testBufferQueueCreation()
+
+ companion object {
+ fun companion_stringFromJNI() {
+ val instance = BufferStreamJNI()
+ instance.stringFromJNI()
+ }
+
+ fun companion_testBufferQueueCreation() {
+ val instance = BufferStreamJNI()
+ instance.testBufferQueueCreation()
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt
new file mode 100644
index 0000000..46ce028
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt
@@ -0,0 +1,33 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun DemoScreen1(modifier: Modifier = Modifier) {
+ Column(modifier = modifier, verticalArrangement = Arrangement.SpaceBetween) {
+ Card(modifier = Modifier.fillMaxWidth().weight(1f, false).padding(16.dp).height(400.dp)) {
+ Text("Log output", modifier = Modifier.padding(16.dp))
+ }
+ Row(modifier = Modifier.weight(1f, false).padding(16.dp)) {
+ Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = { BufferStreamJNI.companion_testBufferQueueCreation() }
+ ) { Text("Run") }
+ OutlinedButton(modifier = Modifier.fillMaxWidth(), onClick = {}) { Text("Clear") }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt
new file mode 100644
index 0000000..5efee92
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt
@@ -0,0 +1,7 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.runtime.Composable
+
+@Composable
+fun DemoScreen2() {
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt
new file mode 100644
index 0000000..8cba857
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt
@@ -0,0 +1,7 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.runtime.Composable
+
+@Composable
+fun DemoScreen3() {
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java
deleted file mode 100644
index 67b95a5..0000000
--- a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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.
-
-package com.android.graphics.bufferstreamsdemoapp;
-
-import android.os.Bundle;
-import android.widget.TextView;
-import androidx.appcompat.app.AppCompatActivity;
-
-public class MainActivity extends AppCompatActivity {
- // Used to load the 'bufferstreamsdemoapp' library on application startup.
- static { System.loadLibrary("bufferstreamdemoapp"); }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- RunBufferQueue();
- System.out.println("stringFromJNI: " + stringFromJNI());
- }
-
- /**
- * A native method that is implemented by the 'bufferstreamsdemoapp' native
- * library, which is packaged with this application.
- */
- public native String stringFromJNI();
- public native void RunBufferQueue();
-}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt
new file mode 100644
index 0000000..f3f4404
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt
@@ -0,0 +1,136 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.annotation.StringRes
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.material3.Button
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import com.android.graphics.bufferstreamsdemoapp.ui.theme.JetpackTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ JetpackTheme {
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background
+ ) { BufferDemosApp() }
+ }
+ }
+ }
+}
+
+enum class BufferDemoScreen(val route: String, @StringRes val title: Int) {
+ Start(route = "start", title = R.string.start),
+ Demo1(route = "demo1", title = R.string.demo1),
+ Demo2(route = "demo2", title = R.string.demo2),
+ Demo3(route = "demo3", title = R.string.demo3);
+
+ companion object {
+ fun findByRoute(route: String): BufferDemoScreen {
+ return values().find { it.route == route }!!
+ }
+ }
+}
+
+@Composable
+fun BufferDemosApp() {
+ var navController: NavHostController = rememberNavController()
+ // Get current back stack entry
+ val backStackEntry by navController.currentBackStackEntryAsState()
+ // Get the name of the current screen
+ val currentScreen =
+ BufferDemoScreen.findByRoute(
+ backStackEntry?.destination?.route ?: BufferDemoScreen.Start.route
+ )
+
+ Scaffold(
+ topBar = {
+ BufferDemosAppBar(
+ currentScreen = currentScreen,
+ canNavigateBack = navController.previousBackStackEntry != null,
+ navigateUp = { navController.navigateUp() }
+ )
+ }
+ ) {
+ NavHost(
+ navController = navController,
+ startDestination = BufferDemoScreen.Start.route,
+ modifier = Modifier.padding(10.dp)
+ ) {
+ composable(route = BufferDemoScreen.Start.route) {
+ DemoList(
+ onButtonClicked = {
+ navController.navigate(it)
+ },
+ )
+ }
+ composable(route = BufferDemoScreen.Demo1.route) {
+ DemoScreen1(modifier = Modifier.fillMaxHeight().padding(top = 100.dp))
+ }
+ composable(route = BufferDemoScreen.Demo2.route) { DemoScreen2() }
+ composable(route = BufferDemoScreen.Demo3.route) { DemoScreen3() }
+ }
+ }
+}
+
+@Composable
+fun DemoList(onButtonClicked: (String) -> Unit) {
+ var modifier = Modifier.fillMaxSize().padding(16.dp)
+
+ Column(modifier = modifier, verticalArrangement = Arrangement.SpaceBetween) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Spacer(modifier = Modifier.height(100.dp))
+ Text(text = "Buffer Demos", style = MaterialTheme.typography.titleLarge)
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+ Row(modifier = Modifier.weight(2f, false)) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ for (item in BufferDemoScreen.values()) {
+ if (item.route != BufferDemoScreen.Start.route)
+ SelectDemoButton(name = stringResource(item.title), onClick = { onButtonClicked(item.route) })
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun SelectDemoButton(name: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
+ Button(onClick = onClick, modifier = modifier.widthIn(min = 250.dp)) { Text(name) }
+}
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt
new file mode 100644
index 0000000..d85ea72
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt
@@ -0,0 +1,11 @@
+package com.android.graphics.bufferstreamsdemoapp.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt
new file mode 100644
index 0000000..fccd93a
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt
@@ -0,0 +1,60 @@
+package com.android.graphics.bufferstreamsdemoapp.ui.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80
+)
+
+private val LightColorScheme = lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40
+)
+
+@Composable
+fun JetpackTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ val window = (view.context as Activity).window
+ window.statusBarColor = colorScheme.primary.toArgb()
+ WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
+ }
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt
new file mode 100644
index 0000000..06814ea
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt
@@ -0,0 +1,18 @@
+package com.android.graphics.bufferstreamsdemoapp.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ )
+)
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/jni/main.cpp b/libs/bufferstreams/examples/app/jni/main.cpp
index 34e0eb4..3d3fee4 100644
--- a/libs/bufferstreams/examples/app/jni/main.cpp
+++ b/libs/bufferstreams/examples/app/jni/main.cpp
@@ -19,16 +19,16 @@
extern "C"
{
JNIEXPORT jstring JNICALL
- Java_com_android_graphics_bufferstreamsdemoapp_MainActivity_stringFromJNI(
- JNIEnv *env,
+ Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_stringFromJNI(
+ JNIEnv* env,
jobject /* this */) {
const char* hello = "Hello from C++";
return env->NewStringUTF(hello);
}
JNIEXPORT void JNICALL
- Java_com_android_graphics_bufferstreamsdemoapp_MainActivity_RunBufferQueue(
- JNIEnv *env,
+ Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_testBufferQueueCreation(
+ JNIEnv* /* env */,
jobject /* this */) {
android::sp<android::IGraphicBufferProducer> producer;
android::sp<android::IGraphicBufferConsumer> consumer;
diff --git a/libs/bufferstreams/examples/app/res/layout/activity_main.xml b/libs/bufferstreams/examples/app/res/layout/activity_main.xml
deleted file mode 100644
index 79fb331..0000000
--- a/libs/bufferstreams/examples/app/res/layout/activity_main.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity">
-
- <TextView
- android:id="@+id/sample_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Hello World!"
- tools:layout_editor_absoluteX="100dp"
- tools:layout_editor_absoluteY="100dp" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/res/values/strings.xml b/libs/bufferstreams/examples/app/res/values/strings.xml
index e652102..75c8ab5 100644
--- a/libs/bufferstreams/examples/app/res/values/strings.xml
+++ b/libs/bufferstreams/examples/app/res/values/strings.xml
@@ -1,3 +1,8 @@
<resources>
<string name="app_name">Buffer Demos</string>
+ <string name="start">Start</string>
+ <string name="demo1">Demo 1</string>
+ <string name="demo2">Demo 2</string>
+ <string name="demo3">Demo 3</string>
+ <string name="back_button">Back</string>
</resources>
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/res/values/themes.xml b/libs/bufferstreams/examples/app/res/values/themes.xml
new file mode 100644
index 0000000..eeb308a
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/values/themes.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="Theme.Jetpack" parent="android:Theme.Material.Light.NoActionBar" />
+</resources>
\ No newline at end of file
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index ed5d5c1..394a000 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -575,17 +575,23 @@
return mAngleNamespace;
}
- if (mAnglePath.empty() && !mShouldUseSystemAngle) {
- ALOGV("mAnglePath is empty and not using system ANGLE, abort creating ANGLE namespace");
+ // If ANGLE path is not set, it means ANGLE should not be used for this process;
+ // or if ANGLE path is set and set to use system ANGLE, then a namespace is not needed
+ // because:
+ // 1) if the default OpenGL ES driver is already ANGLE, then the loader will skip;
+ // 2) if the default OpenGL ES driver is native, then there's no symbol conflict;
+ // 3) if there's no OpenGL ES driver is preloaded, then there's no symbol conflict.
+ if (mAnglePath.empty() || mShouldUseSystemAngle) {
+ ALOGV("mAnglePath is empty or use system ANGLE, abort creating ANGLE namespace");
return nullptr;
}
// Construct the search paths for system ANGLE.
const char* const defaultLibraryPaths =
#if defined(__LP64__)
- "/vendor/lib64/egl:/system/lib64/egl";
+ "/vendor/lib64/egl:/system/lib64";
#else
- "/vendor/lib/egl:/system/lib/egl";
+ "/vendor/lib/egl:/system/lib";
#endif
// If the application process will run on top of system ANGLE, construct the namespace
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 47607a0..9ebaf16 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -104,7 +104,7 @@
GL_UPDATED = 2,
VULKAN = 3,
VULKAN_UPDATED = 4,
- ANGLE = 5,
+ ANGLE = 5, // cover both system ANGLE and ANGLE APK
};
enum Stats {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index f17a654..c84ee1f 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -20,6 +20,25 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "libgui_flags",
+ package: "com.android.graphics.libgui.flags",
+ srcs: ["libgui_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "libguiflags",
+ host_supported: true,
+ vendor_available: true,
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ ],
+ aconfig_declarations: "libgui_flags",
+}
+
cc_library_headers {
name: "libgui_headers",
vendor_available: true,
@@ -36,6 +55,8 @@
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
],
+ static_libs: ["libguiflags"],
+ export_static_lib_headers: ["libguiflags"],
min_sdk_version: "29",
// TODO(b/218719284) can media use be constrained to libgui_bufferqueue_static?
apex_available: [
@@ -192,19 +213,6 @@
},
}
-aconfig_declarations {
- name: "libgui_flags",
- package: "com.android.graphics.libgui.flags",
- srcs: ["libgui_flags.aconfig"],
-}
-
-cc_aconfig_library {
- name: "libguiflags",
- host_supported: true,
- vendor_available: true,
- aconfig_declarations: "libgui_flags",
-}
-
filegroup {
name: "libgui-sources",
srcs: [
@@ -265,6 +273,9 @@
"libbinder",
"libGLESv2",
],
+ export_static_lib_headers: [
+ "libguiflags",
+ ],
}
cc_library_shared {
@@ -460,6 +471,7 @@
static_libs: [
"libgtest",
"libgmock",
+ "libguiflags",
],
srcs: [
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index dd0a028..8d0331e 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -26,7 +26,7 @@
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
-#include <gui/Flags.h>
+
#include <gui/FrameRateUtils.h>
#include <gui/GLConsumer.h>
#include <gui/IProducerListener.h>
@@ -41,8 +41,6 @@
#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;
@@ -144,7 +142,7 @@
}
}
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
void BLASTBufferItemConsumer::onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index ab0f6d2..b0f6e69 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -22,7 +22,6 @@
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
-#include <gui/Flags.h>
namespace android {
@@ -99,7 +98,7 @@
}
}
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
void BufferQueue::ProxyConsumerListener::onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
sp<ConsumerListener> listener(mConsumerListener.promote());
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 5b34ba1..b6a47fb 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -318,35 +318,44 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot);
- std::lock_guard<std::mutex> lock(mCore->mMutex);
+ sp<IProducerListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
- return NO_INIT;
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mSharedBufferMode || slot == mCore->mSharedBufferSlot) {
+ BQ_LOGE("detachBuffer: detachBuffer not allowed in shared buffer mode");
+ return BAD_VALUE;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
+ slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mBufferState.isAcquired()) {
+ BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
+ return BAD_VALUE;
+ }
+ if (mCore->mBufferReleasedCbEnabled) {
+ listener = mCore->mConnectedProducerListener;
+ }
+
+ mSlots[slot].mBufferState.detachConsumer();
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeSlots.insert(slot);
+ mCore->clearBufferSlotLocked(slot);
+ mCore->mDequeueCondition.notify_all();
+ VALIDATE_CONSISTENCY();
}
- if (mCore->mSharedBufferMode || slot == mCore->mSharedBufferSlot) {
- BQ_LOGE("detachBuffer: detachBuffer not allowed in shared buffer mode");
- return BAD_VALUE;
+ if (listener) {
+ listener->onBufferDetached(slot);
}
-
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
- } else if (!mSlots[slot].mBufferState.isAcquired()) {
- BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- return BAD_VALUE;
- }
-
- mSlots[slot].mBufferState.detachConsumer();
- mCore->mActiveBuffers.erase(slot);
- mCore->mFreeSlots.insert(slot);
- mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.notify_all();
- VALIDATE_CONSISTENCY();
-
return NO_ERROR;
}
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 67dff6d..19693e3 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -32,7 +32,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
-#include <gui/Flags.h>
+
#include <gui/FrameRateUtils.h>
#include <gui/GLConsumer.h>
#include <gui/IConsumerListener.h>
@@ -1753,7 +1753,7 @@
return NO_ERROR;
}
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
status_t BufferQueueProducer::setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
ATRACE_CALL();
diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp
index 6993bfa..11524e2 100644
--- a/libs/gui/FrameRateUtils.cpp
+++ b/libs/gui/FrameRateUtils.cpp
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#include <gui/Flags.h>
#include <gui/FrameRateUtils.h>
#include <system/window.h>
#include <utils/Log.h>
#include <cmath>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
+using namespace com::android::graphics::libgui;
// Returns true if the frameRate is valid.
//
// @param frameRate the frame rate in Hz
@@ -53,7 +55,7 @@
changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) {
ALOGE("%s failed - invalid change frame rate strategy value %d", functionName,
changeFrameRateStrategy);
- if (FLAG_BQ_SET_FRAME_RATE) {
+ if (flags::bq_setframerate()) {
return false;
}
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index d0c09e4..e81c098 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -28,7 +28,7 @@
#include <binder/IInterface.h>
#include <gui/BufferQueueDefs.h>
-#include <gui/Flags.h>
+
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
@@ -763,7 +763,7 @@
}
return result;
}
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
virtual status_t setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override {
Parcel data, reply;
@@ -973,7 +973,7 @@
return INVALID_OPERATION;
}
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
status_t IGraphicBufferProducer::setFrameRate(float /*frameRate*/, int8_t /*compatibility*/,
int8_t /*changeFrameRateStrategy*/) {
// No-op for IGBP other than BufferQueue.
@@ -1522,7 +1522,7 @@
reply->writeInt32(result);
return NO_ERROR;
}
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
case SET_FRAME_RATE: {
CHECK_INTERFACE(IGraphicBuffer, data, reply);
float frameRate = data.readFloat();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index a87f053..07a0cfe 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -43,7 +43,7 @@
#include <gui/AidlStatusUtil.h>
#include <gui/BufferItem.h>
-#include <gui/Flags.h>
+
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
@@ -2571,7 +2571,7 @@
status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
if (flags::bq_setframerate()) {
status_t err = mGraphicBufferProducer->setFrameRate(frameRate, compatibility,
changeFrameRateStrategy);
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 2eb6bd6..6a4460b 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -66,8 +66,9 @@
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 @@
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 = 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/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 02d7c4d..892215e 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -19,7 +19,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
-#include <gui/Flags.h>
+
#include <gui/IGraphicBufferProducer.h>
#include <gui/SurfaceComposerClient.h>
@@ -31,6 +31,8 @@
#include <thread>
#include <queue>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
class BLASTBufferQueue;
@@ -59,7 +61,7 @@
protected:
void onSidebandStreamChanged() override EXCLUDES(mMutex);
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
void onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override;
#endif
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 2756277..0948c4d0 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -19,11 +19,13 @@
#include <gui/BufferItem.h>
#include <gui/BufferQueueDefs.h>
-#include <gui/Flags.h>
+
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
class BufferQueue {
@@ -70,7 +72,7 @@
void addAndGetFrameTimestamps(
const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) override;
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
void onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override;
#endif
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 38805d0..de47483 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -18,7 +18,7 @@
#define ANDROID_GUI_BUFFERQUEUEPRODUCER_H
#include <gui/BufferQueueDefs.h>
-#include <gui/Flags.h>
+
#include <gui/IGraphicBufferProducer.h>
namespace android {
@@ -202,7 +202,7 @@
// See IGraphicBufferProducer::setAutoPrerotation
virtual status_t setAutoPrerotation(bool autoPrerotation);
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
// See IGraphicBufferProducer::setFrameRate
status_t setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override;
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
deleted file mode 100644
index a2cff56..0000000
--- a/libs/gui/include/gui/Flags.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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
-
-// TODO(281695725): replace this with build time flags, whenever they are available
-#ifndef FLAG_BQ_SET_FRAME_RATE
-#define FLAG_BQ_SET_FRAME_RATE false
-#endif
\ No newline at end of file
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index e183bf2..51d3959 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -19,13 +19,13 @@
#include <binder/IInterface.h>
#include <binder/SafeInterface.h>
-#include <gui/Flags.h>
-
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <cstdint>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
class BufferItem;
@@ -93,7 +93,7 @@
virtual void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/,
FrameEventHistoryDelta* /*outDelta*/) {}
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
// Notifies the consumer of a setFrameRate call from the producer side.
virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/,
int8_t /*changeFrameRateStrategy*/) {}
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 3562906..7639e70 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -31,7 +31,6 @@
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <gui/Flags.h>
#include <gui/FrameTimestamps.h>
#include <gui/HdrMetadata.h>
@@ -42,6 +41,8 @@
#include <optional>
#include <vector>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -677,7 +678,7 @@
// the width and height used for dequeueBuffer will be additionally swapped.
virtual status_t setAutoPrerotation(bool autoPrerotation);
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
// Sets the apps intended frame rate.
virtual status_t setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy);
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index f7ffbb9..b15f501 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -49,6 +49,12 @@
// onBuffersFreed is called from IGraphicBufferConsumer::discardFreeBuffers
// to notify the producer that certain free buffers are discarded by the consumer.
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous
+ // onBufferDetached is called from IGraphicBufferConsumer::detachBuffer to
+ // notify the producer that a buffer slot is free and ready to be dequeued.
+ //
+ // This is called without any lock held and can be called concurrently by
+ // multiple threads.
+ virtual void onBufferDetached(int /*slot*/) {} // Asynchronous
};
#ifndef NO_BINDER
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index bd2eb74..dcc38d7 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 @@
/* 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/tests/Android.bp b/libs/gui/tests/Android.bp
index 38c0eed..6dcd501 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -21,7 +21,7 @@
"-Wall",
"-Werror",
"-Wno-extra",
- "-DFLAG_BQ_SET_FRAME_RATE=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
],
srcs: [
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 17aa5f1..df7739c 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -1187,6 +1187,76 @@
ASSERT_EQ(true, output.bufferReplaced);
}
+struct BufferDetachedListener : public BnProducerListener {
+public:
+ BufferDetachedListener() = default;
+ virtual ~BufferDetachedListener() = default;
+
+ virtual void onBufferReleased() {}
+ virtual bool needsReleaseNotify() { return true; }
+ virtual void onBufferDetached(int slot) {
+ mDetachedSlots.push_back(slot);
+ }
+ const std::vector<int>& getDetachedSlots() const { return mDetachedSlots; }
+private:
+ std::vector<int> mDetachedSlots;
+};
+
+TEST_F(BufferQueueTest, TestConsumerDetachProducerListener) {
+ createBufferQueue();
+ sp<MockConsumer> mc(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<BufferDetachedListener> pl(new BufferDetachedListener);
+ ASSERT_EQ(OK, mProducer->connect(pl, NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK, mProducer->setDequeueTimeout(0));
+ ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
+
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+
+ int slots[2] = {};
+ status_t result = OK;
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
+
+ result = mProducer->dequeueBuffer(&slots[0], &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[0], &buffer));
+
+ result = mProducer->dequeueBuffer(&slots[1], &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[1], &buffer));
+
+ // Queue & detach one from two dequeued buffes.
+ ASSERT_EQ(OK, mProducer->queueBuffer(slots[1], input, &output));
+ BufferItem item{};
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->detachBuffer(item.mSlot));
+
+ // Check whether the slot from IProducerListener is same to the detached slot.
+ ASSERT_EQ(pl->getDetachedSlots().size(), 1);
+ ASSERT_EQ(pl->getDetachedSlots()[0], slots[1]);
+
+ // Dequeue another buffer.
+ int slot;
+ result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+
+ // Dequeue should fail here, since we dequeued 3 buffers and one buffer was
+ // detached from consumer(Two buffers are dequeued, and the current max
+ // dequeued buffer count is two).
+ result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_TRUE(result == WOULD_BLOCK || result == TIMED_OUT || result == INVALID_OPERATION);
+}
+
TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
createBufferQueue();
sp<MockConsumer> mc(new MockConsumer);
@@ -1266,9 +1336,7 @@
}
TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) {
- if (flags::bq_setframerate()) {
- ASSERT_EQ(true, FLAG_BQ_SET_FRAME_RATE);
- }
+ ASSERT_EQ(flags::bq_setframerate(), COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE));
}
struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e7b1232..0e26544 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -208,21 +208,6 @@
ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
}
- static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults) {
- const auto sf = ComposerServiceAIDL::getComposerService();
- SurfaceComposerClient::Transaction().apply(true);
-
- const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- binder::Status status = sf->captureDisplay(captureArgs, captureListener);
- status_t err = gui::aidl_utils::statusTFromBinderStatus(status);
- if (err != NO_ERROR) {
- return err;
- }
- captureResults = captureListener->waitForResults();
- return fenceStatus(captureResults.fenceResult);
- }
-
sp<Surface> mSurface;
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mSurfaceControl;
@@ -260,56 +245,6 @@
EXPECT_EQ(1, result);
}
-// This test probably doesn't belong here.
-TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) {
- sp<ANativeWindow> anw(mSurface);
-
- // Verify the screenshot works with no protected buffers.
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- // display 0 is picked for now, can extend to support all displays if needed
- const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- ASSERT_FALSE(display == nullptr);
-
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = display;
- captureArgs.width = 64;
- captureArgs.height = 64;
-
- ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
-
- ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
- NATIVE_WINDOW_API_CPU));
- // Set the PROTECTED usage bit and verify that the screenshot fails. Note
- // that we need to dequeue a buffer in order for it to actually get
- // allocated in SurfaceFlinger.
- ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(),
- GRALLOC_USAGE_PROTECTED));
- ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3));
- ANativeWindowBuffer* buf = nullptr;
-
- status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf);
- if (err) {
- // we could fail if GRALLOC_USAGE_PROTECTED is not supported.
- // that's okay as long as this is the reason for the failure.
- // try again without the GRALLOC_USAGE_PROTECTED bit.
- ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0));
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
- &buf));
- return;
- }
- ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf, -1));
-
- for (int i = 0; i < 4; i++) {
- // Loop to make sure SurfaceFlinger has retired a protected buffer.
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
- &buf));
- ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
- }
- ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
-}
-
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
sp<ANativeWindow> anw(mSurface);
int result = -123;
@@ -1341,7 +1276,7 @@
newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime :
FenceTime::NO_FENCE;
// HWC2 releases the previous buffer after a new latch just before
- // calling postComposition.
+ // calling onCompositionPresented.
if (oldFrame != nullptr) {
mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime,
std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime));
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index f2feaef..5eb5d3b 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -28,6 +28,7 @@
using gui::InputApplicationInfo;
using gui::TouchOcclusionMode;
using gui::WindowInfo;
+using ui::Size;
namespace test {
@@ -53,6 +54,7 @@
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 @@
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 69a4f0a..dd8dc8d 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -37,20 +37,20 @@
// flags
/////////////////////////////////////////////////
aconfig_declarations {
- name: "aconfig_input_flags",
+ name: "com.android.input.flags-aconfig",
package: "com.android.input.flags",
srcs: ["input_flags.aconfig"],
}
cc_aconfig_library {
- name: "aconfig_input_flags_c_lib",
- aconfig_declarations: "aconfig_input_flags",
+ name: "com.android.input.flags-aconfig-cc",
+ aconfig_declarations: "com.android.input.flags-aconfig",
host_supported: true,
// Use the test version of the aconfig flag library by default to allow tests to set local
// overrides for flags, without having to link against a separate version of libinput or of this
// library. Bundling this library directly into libinput prevents us from having to add this
// library as a shared lib dependency everywhere where libinput is used.
- test: true,
+ mode: "test",
shared: {
enabled: false,
},
@@ -242,7 +242,7 @@
],
whole_static_libs: [
- "aconfig_input_flags_c_lib",
+ "com.android.input.flags-aconfig-cc",
"libinput_rust_ffi",
],
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index a4cd239..e2feabc 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -613,14 +613,14 @@
}
#ifdef __linux__
-std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
+std::unique_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
if (parcel == nullptr) {
ALOGE("%s: Null parcel", __func__);
return nullptr;
}
std::string loadFileName = parcel->readCString();
- std::shared_ptr<KeyCharacterMap> map =
- std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(loadFileName));
+ std::unique_ptr<KeyCharacterMap> map =
+ std::make_unique<KeyCharacterMap>(KeyCharacterMap(loadFileName));
map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
map->mLayoutOverlayApplied = parcel->readBool();
size_t numKeys = parcel->readInt32();
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 116b778..613a0df 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -275,10 +275,10 @@
}
}
-void VelocityTracker::addMovement(const MotionEvent* event) {
+void VelocityTracker::addMovement(const MotionEvent& event) {
// Stores data about which axes to process based on the incoming motion event.
std::set<int32_t> axesToProcess;
- int32_t actionMasked = event->getActionMasked();
+ int32_t actionMasked = event.getActionMasked();
switch (actionMasked) {
case AMOTION_EVENT_ACTION_DOWN:
@@ -291,7 +291,7 @@
// Start a new movement trace for a pointer that just went down.
// We do this on down instead of on up because the client may want to query the
// final velocity for a pointer that just went up.
- clearPointer(event->getPointerId(event->getActionIndex()));
+ clearPointer(event.getPointerId(event.getActionIndex()));
axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
break;
}
@@ -300,8 +300,14 @@
axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
break;
case AMOTION_EVENT_ACTION_POINTER_UP:
+ if (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) {
+ clearPointer(event.getPointerId(event.getActionIndex()));
+ return;
+ }
+ // Continue to ACTION_UP to ensure that the POINTER_STOPPED logic is triggered.
+ [[fallthrough]];
case AMOTION_EVENT_ACTION_UP: {
- std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime);
+ std::chrono::nanoseconds delaySinceLastEvent(event.getEventTime() - mLastEventTime);
if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) {
ALOGD_IF(DEBUG_VELOCITY,
"VelocityTracker: stopped for %s, clearing state upon pointer liftoff.",
@@ -325,21 +331,26 @@
case AMOTION_EVENT_ACTION_SCROLL:
axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL);
break;
+ case AMOTION_EVENT_ACTION_CANCEL: {
+ clear();
+ return;
+ }
+
default:
// Ignore all other actions.
return;
}
- const size_t historySize = event->getHistorySize();
+ const size_t historySize = event.getHistorySize();
for (size_t h = 0; h <= historySize; h++) {
- const nsecs_t eventTime = event->getHistoricalEventTime(h);
- for (size_t i = 0; i < event->getPointerCount(); i++) {
- if (event->isResampled(i, h)) {
+ const nsecs_t eventTime = event.getHistoricalEventTime(h);
+ for (size_t i = 0; i < event.getPointerCount(); i++) {
+ if (event.isResampled(i, h)) {
continue; // skip resampled samples
}
- const int32_t pointerId = event->getPointerId(i);
+ const int32_t pointerId = event.getPointerId(i);
for (int32_t axis : axesToProcess) {
- const float position = event->getHistoricalAxisValue(axis, i, h);
+ const float position = event.getHistoricalAxisValue(axis, i, h);
addMovement(eventTime, pointerId, axis, position);
}
}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 6302ff5..3672387 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -41,3 +41,24 @@
description: "Brings back fatal logging for inconsistent event streams originating from accessibility."
bug: "299977100"
}
+
+flag {
+ name: "enable_touchpad_typing_palm_rejection"
+ namespace: "input"
+ description: "Enable additional palm rejection on touchpad while typing"
+ bug: "301055381"
+}
+
+flag {
+ name: "remove_app_switch_drops"
+ namespace: "input"
+ description: "Remove the logic of dropping events due to pending app switch"
+ bug: "284808102"
+}
+
+flag {
+ name: "disable_reject_touch_on_stylus_hover"
+ namespace: "input"
+ description: "Disable touch rejection when the stylus hovers the screen"
+ bug: "301216095"
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index bbc6d98..5f05a0f 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -118,18 +118,17 @@
match action.into() {
MotionAction::Down => {
+ if self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
+ self.name, device_id, self.touching_pointer_ids_by_device
+ ));
+ }
let it = self
.touching_pointer_ids_by_device
.entry(device_id)
.or_insert_with(HashSet::new);
- let pointer_id = pointer_properties[0].id;
- if it.contains(&pointer_id) {
- return Err(format!(
- "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
- self.name, device_id, it
- ));
- }
- it.insert(pointer_id);
+ it.insert(pointer_properties[0].id);
}
MotionAction::PointerDown { action_index } => {
if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
@@ -353,6 +352,56 @@
}
#[test]
+ fn two_pointer_stream() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ // POINTER 1 DOWN
+ let two_pointer_properties =
+ Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
+ | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ &two_pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ // POINTER 0 UP
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP
+ | (0 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ &two_pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ // ACTION_UP for pointer id=1
+ let pointer_1_properties = Vec::from([RustPointerProperties { id: 1 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_1_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
fn multi_device_stream() {
let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 1c8ec90..f9ca280 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -229,41 +229,23 @@
return events;
}
-static std::optional<float> computePlanarVelocity(
- const VelocityTracker::Strategy strategy,
- const std::vector<PlanarMotionEventEntry>& motions, int32_t axis,
- uint32_t pointerId = DEFAULT_POINTER_ID) {
+static std::optional<float> computeVelocity(const VelocityTracker::Strategy strategy,
+ const std::vector<MotionEvent>& events, int32_t axis,
+ uint32_t pointerId = DEFAULT_POINTER_ID) {
VelocityTracker vt(strategy);
- std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
- for (MotionEvent event : events) {
- vt.addMovement(&event);
+ for (const MotionEvent& event : events) {
+ vt.addMovement(event);
}
return vt.getVelocity(axis, pointerId);
}
-static std::vector<MotionEvent> createMotionEventStream(
- int32_t axis, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motion) {
- switch (axis) {
- case AMOTION_EVENT_AXIS_SCROLL:
- return createAxisScrollMotionEventStream(motion);
- default:
- ADD_FAILURE() << "Axis " << axis << " is not supported";
- return {};
- }
-}
-
-static std::optional<float> computeVelocity(
+static std::optional<float> computePlanarVelocity(
const VelocityTracker::Strategy strategy,
- const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, int32_t axis) {
- VelocityTracker vt(strategy);
-
- for (const MotionEvent& event : createMotionEventStream(axis, motions)) {
- vt.addMovement(&event);
- }
-
- return vt.getVelocity(axis, DEFAULT_POINTER_ID);
+ const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, uint32_t pointerId) {
+ std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
+ return computeVelocity(strategy, events, axis, pointerId);
}
static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
@@ -277,23 +259,23 @@
const VelocityTracker::Strategy strategy,
const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions,
std::optional<float> targetVelocity) {
- checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
+ std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions);
+ checkVelocity(computeVelocity(strategy, events, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
// The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall
// back to a strategy that supports differential axes.
- checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions,
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events,
AMOTION_EVENT_AXIS_SCROLL),
targetVelocity);
}
static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions,
float velocity) {
- VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
- std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
- for (MotionEvent event : events) {
- vt.addMovement(&event);
- }
- std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0);
- std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
+ std::optional<float> velocityX =
+ computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ DEFAULT_POINTER_ID);
+ std::optional<float> velocityY =
+ computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ DEFAULT_POINTER_ID);
ASSERT_TRUE(velocityX);
ASSERT_TRUE(velocityY);
@@ -330,12 +312,14 @@
{30ms, {{6, 20}}},
{40ms, {{10, 30}}}};
- EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X),
+ EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ DEFAULT_POINTER_ID),
computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions,
- AMOTION_EVENT_AXIS_X));
- EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y),
+ AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
+ EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ DEFAULT_POINTER_ID),
computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions,
- AMOTION_EVENT_AXIS_Y));
+ AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID));
}
TEST_F(VelocityTrackerTest, TestComputedVelocity) {
@@ -431,7 +415,7 @@
VelocityTracker vt(VelocityTracker::Strategy::IMPULSE);
std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
for (const MotionEvent& event : events) {
- vt.addMovement(&event);
+ vt.addMovement(event);
}
float maxFloat = std::numeric_limits<float>::max();
@@ -509,6 +493,89 @@
computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
}
+/**
+ * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0.
+ */
+TEST_F(VelocityTrackerTest, ActionCancelResultsInZeroVelocity) {
+ std::vector<PlanarMotionEventEntry> motions = {
+ {0ms, {{0, 0}}}, // DOWN
+ {10ms, {{5, 10}}}, // MOVE
+ {20ms, {{10, 20}}}, // MOVE
+ {20ms, {{10, 20}}}, // ACTION_UP
+ };
+ std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
+ // By default, `createTouchMotionEventStream` produces an event stream that terminates with
+ // ACTION_UP. We need to manually change it to ACTION_CANCEL.
+ MotionEvent& lastEvent = events.back();
+ lastEvent.setAction(AMOTION_EVENT_ACTION_CANCEL);
+ lastEvent.setFlags(lastEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED);
+ const int32_t pointerId = lastEvent.getPointerId(0);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X,
+ pointerId),
+ /*targetVelocity*/ std::nullopt);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y,
+ pointerId),
+ /*targetVelocity*/ std::nullopt);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X,
+ pointerId),
+ /*targetVelocity*/ std::nullopt);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y,
+ pointerId),
+ /*targetVelocity*/ std::nullopt);
+}
+
+/**
+ * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0.
+ */
+TEST_F(VelocityTrackerTest, ActionPointerCancelResultsInZeroVelocityForThatPointer) {
+ std::vector<PlanarMotionEventEntry> motions = {
+ {0ms, {{0, 5}, {NAN, NAN}}}, // DOWN
+ {0ms, {{0, 5}, {10, 15}}}, // POINTER_DOWN
+ {10ms, {{5, 10}, {15, 20}}}, // MOVE
+ {20ms, {{10, 15}, {20, 25}}}, // MOVE
+ {30ms, {{10, 15}, {20, 25}}}, // POINTER_UP
+ {30ms, {{10, 15}, {NAN, NAN}}}, // UP
+ };
+ std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
+ // Cancel the lifting pointer of the ACTION_POINTER_UP event
+ MotionEvent& pointerUpEvent = events.rbegin()[1];
+ pointerUpEvent.setFlags(pointerUpEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED);
+ const int32_t pointerId = pointerUpEvent.getPointerId(pointerUpEvent.getActionIndex());
+ // Double check the stream
+ ASSERT_EQ(1, pointerId);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP, pointerUpEvent.getActionMasked());
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, events.back().getActionMasked());
+
+ // Ensure the velocity of the lifting pointer is zero
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X,
+ pointerId),
+ /*targetVelocity*/ std::nullopt);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y,
+ pointerId),
+ /*targetVelocity*/ std::nullopt);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X,
+ pointerId),
+ /*targetVelocity*/ std::nullopt);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y,
+ pointerId),
+ /*targetVelocity*/ std::nullopt);
+
+ // The remaining pointer should have the correct velocity.
+ const int32_t remainingPointerId = events.back().getPointerId(0);
+ ASSERT_EQ(0, remainingPointerId);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X,
+ remainingPointerId),
+ /*targetVelocity*/ 500);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y,
+ remainingPointerId),
+ /*targetVelocity*/ 500);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X,
+ remainingPointerId),
+ /*targetVelocity*/ 500);
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y,
+ remainingPointerId),
+ /*targetVelocity*/ 500);
+}
/**
* ================== VelocityTracker tests generated by recording real events =====================
@@ -1336,9 +1403,10 @@
{40ms, 100},
};
- EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, motions,
+ std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions);
+ EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, events,
AMOTION_EVENT_AXIS_SCROLL),
- computeVelocity(VelocityTracker::Strategy::DEFAULT, motions,
+ computeVelocity(VelocityTracker::Strategy::DEFAULT, events,
AMOTION_EVENT_AXIS_SCROLL));
}
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 32fb350..099f47d 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -19,7 +19,7 @@
#include <android/hardware_buffer.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
-#include <gui/Flags.h>
+
#include <gui/IGraphicBufferProducer.h>
#include <sys/cdefs.h>
#include <system/graphics.h>
@@ -352,7 +352,7 @@
/**
* onSetFrameRate Notifies the consumer of a setFrameRate call from the producer side.
*/
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
void onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override;
#endif
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index c2535e0..3a09204 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -515,7 +515,7 @@
}
}
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
void SurfaceTexture::onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
SFT_LOGV("onSetFrameRate: %.2f", frameRate);
diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h
index a252245..0d5727d 100644
--- a/libs/nativewindow/include/android/native_window_aidl.h
+++ b/libs/nativewindow/include/android/native_window_aidl.h
@@ -34,6 +34,12 @@
#include <android/native_window.h>
#include <sys/cdefs.h>
+// Only required by the AIDL glue helper
+#ifdef __cplusplus
+#include <sstream>
+#include <string>
+#endif // __cplusplus
+
__BEGIN_DECLS
/**
@@ -80,7 +86,7 @@
* Takes ownership of the ANativeWindow* given to it in reset() and will automatically
* destroy it in the destructor, similar to a smart pointer container
*/
-class NativeWindow {
+class NativeWindow final {
public:
NativeWindow() noexcept {}
explicit NativeWindow(ANativeWindow* _Nullable window) {
@@ -123,15 +129,29 @@
}
mWindow = window;
}
- inline ANativeWindow* _Nullable operator-> () const { return mWindow; }
+
inline ANativeWindow* _Nullable get() const { return mWindow; }
- inline explicit operator bool () const { return mWindow != nullptr; }
NativeWindow& operator=(NativeWindow&& other) noexcept {
mWindow = other.release(); // steal ownership from r-value
return *this;
}
+ inline ANativeWindow* _Nullable operator->() const { return mWindow; }
+ inline explicit operator bool() const { return mWindow != nullptr; }
+ inline bool operator==(const NativeWindow& rhs) const { return mWindow == rhs.mWindow; }
+ inline bool operator!=(const NativeWindow& rhs) const { return !(*this == rhs); }
+ inline bool operator<(const NativeWindow& rhs) const { return mWindow < rhs.mWindow; }
+ inline bool operator>(const NativeWindow& rhs) const { return rhs < *this; }
+ inline bool operator>=(const NativeWindow& rhs) const { return !(*this < rhs); }
+ inline bool operator<=(const NativeWindow& rhs) const { return !(*this > rhs); }
+
+ std::string toString() const {
+ std::ostringstream ss;
+ ss << "NativeWindow: " << mWindow;
+ return ss.str();
+ }
+
/**
* Stops managing any contained ANativeWindow*, returning it to the caller. Ownership
* is released.
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 538c1d2..4246c40 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -363,9 +363,9 @@
}
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 @@
// 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 b15694b..4454f36 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -72,6 +72,10 @@
llndk: {
llndk_headers: true,
},
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
subdirs = [
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 04e2fff..e487cbc 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -66,6 +66,8 @@
static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl";
static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform";
+static const char* ANGLE_SUFFIX_VALUE = "angle";
+static const char* VENDOR_ANGLE_BUILD = "ro.gfx.angle.supported";
static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = {
PERSIST_DRIVER_SUFFIX_PROPERTY,
@@ -80,6 +82,13 @@
"/vendor/lib/egl";
#endif
+static const char* const SYSTEM_LIB_DIR =
+#if defined(__LP64__)
+ "/system/lib64";
+#else
+ "/system/lib";
+#endif
+
static void* do_dlopen(const char* path, int mode) {
ATRACE_CALL();
return dlopen(path, mode);
@@ -434,98 +443,110 @@
}
}
+static std::string findLibrary(const std::string libraryName, const std::string searchPath,
+ const bool exact) {
+ if (exact) {
+ std::string absolutePath = searchPath + "/" + libraryName + ".so";
+ if (!access(absolutePath.c_str(), R_OK)) {
+ return absolutePath;
+ }
+ return std::string();
+ }
+
+ DIR* d = opendir(searchPath.c_str());
+ if (d != nullptr) {
+ struct dirent* e;
+ while ((e = readdir(d)) != nullptr) {
+ if (e->d_type == DT_DIR) {
+ continue;
+ }
+ if (!strcmp(e->d_name, "libGLES_android.so")) {
+ // always skip the software renderer
+ continue;
+ }
+ if (strstr(e->d_name, libraryName.c_str()) == e->d_name) {
+ if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {
+ std::string result = searchPath + "/" + e->d_name;
+ closedir(d);
+ return result;
+ }
+ }
+ }
+ closedir(d);
+ }
+ // Driver not found. gah.
+ return std::string();
+}
+
static void* load_system_driver(const char* kind, const char* suffix, const bool exact) {
ATRACE_CALL();
- class MatchFile {
- public:
- static std::string find(const char* libraryName, const bool exact) {
- std::string absolutePath;
- if (findLibPath(absolutePath, libraryName, exact)) {
- return absolutePath;
- }
-
- // Driver not found. gah.
- return std::string();
- }
- private:
- static bool findLibPath(std::string& result, const std::string& pattern, bool exact) {
- const std::string vendorLibEglDirString = std::string(VENDOR_LIB_EGL_DIR);
- if (exact) {
- std::string absolutePath = vendorLibEglDirString + "/" + pattern + ".so";
- if (!access(absolutePath.c_str(), R_OK)) {
- result = absolutePath;
- return true;
- }
- return false;
- }
-
- DIR* d = opendir(VENDOR_LIB_EGL_DIR);
- if (d != nullptr) {
- struct dirent* e;
- while ((e = readdir(d)) != nullptr) {
- if (e->d_type == DT_DIR) {
- continue;
- }
- if (!strcmp(e->d_name, "libGLES_android.so")) {
- // always skip the software renderer
- continue;
- }
- if (strstr(e->d_name, pattern.c_str()) == e->d_name) {
- if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {
- result = vendorLibEglDirString + "/" + e->d_name;
- closedir(d);
- return true;
- }
- }
- }
- closedir(d);
- }
- return false;
- }
- };
std::string libraryName = std::string("lib") + kind;
if (suffix) {
libraryName += std::string("_") + suffix;
} else if (!exact) {
- // Deprecated: we look for files that match
- // libGLES_*.so, or:
+ // Deprecated for devices launching in Android 14
+ // Look for files that match
+ // libGLES_*.so, or,
// libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so
libraryName += std::string("_");
}
- std::string absolutePath = MatchFile::find(libraryName.c_str(), exact);
+
+ void* dso = nullptr;
+
+ const bool AngleInVendor = property_get_bool(VENDOR_ANGLE_BUILD, false);
+ const bool isSuffixAngle = suffix != nullptr && strcmp(suffix, ANGLE_SUFFIX_VALUE) == 0;
+ // Only use sphal namespace when system ANGLE binaries are not the default drivers.
+ const bool useSphalNamespace = !isSuffixAngle || AngleInVendor;
+
+ const std::string absolutePath =
+ findLibrary(libraryName, useSphalNamespace ? VENDOR_LIB_EGL_DIR : SYSTEM_LIB_PATH,
+ exact);
if (absolutePath.empty()) {
// this happens often, we don't want to log an error
return nullptr;
}
- const char* const driver_absolute_path = absolutePath.c_str();
+ const char* const driverAbsolutePath = absolutePath.c_str();
- // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to
- // the original routine when the namespace does not exist.
- // See /system/core/rootdir/etc/ld.config.txt for the configuration of the
- // sphal namespace.
- void* dso = do_android_load_sphal_library(driver_absolute_path,
- RTLD_NOW | RTLD_LOCAL);
+ // Currently the default driver is unlikely to be ANGLE on most devices,
+ // hence put this first.
+ if (useSphalNamespace) {
+ // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to
+ // the original routine when the namespace does not exist.
+ // See /system/linkerconfig/contents/namespace for the configuration of the
+ // sphal namespace.
+ dso = do_android_load_sphal_library(driverAbsolutePath, RTLD_NOW | RTLD_LOCAL);
+ } else {
+ // Try to load drivers from the default namespace.
+ // See /system/linkerconfig/contents/namespace for the configuration of the
+ // default namespace.
+ dso = do_dlopen(driverAbsolutePath, RTLD_NOW | RTLD_LOCAL);
+ }
+
if (dso == nullptr) {
const char* err = dlerror();
- ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown");
+ ALOGE("load_driver(%s): %s", driverAbsolutePath, err ? err : "unknown");
return nullptr;
}
- ALOGD("loaded %s", driver_absolute_path);
+ ALOGV("loaded %s", driverAbsolutePath);
return dso;
}
static void* load_angle(const char* kind, android_namespace_t* ns) {
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = ns,
- };
-
std::string name = std::string("lib") + kind + "_angle.so";
+ void* so = nullptr;
- void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+ if (android::GraphicsEnv::getInstance().shouldUseSystemAngle()) {
+ so = do_dlopen(name.c_str(), RTLD_NOW | RTLD_LOCAL);
+ } else {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = ns,
+ };
+ so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+ }
if (so) {
return so;
@@ -563,10 +584,14 @@
ATRACE_CALL();
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
- if (!ns) {
+ // ANGLE namespace is used for loading ANGLE from apk, and hence if namespace is not
+ // constructed, it means ANGLE apk is not set to be the OpenGL ES driver.
+ // Hence skip if ANGLE apk and system ANGLE are not set to be the OpenGL ES driver.
+ if (!ns && !android::GraphicsEnv::getInstance().shouldUseSystemAngle()) {
return nullptr;
}
+ // use ANGLE APK driver
android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE);
driver_t* hnd = nullptr;
@@ -588,10 +613,13 @@
}
void Loader::attempt_to_init_angle_backend(void* dso, egl_connection_t* cnx) {
- void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
- if (pANGLEGetDisplayPlatform) {
+ cnx->angleGetDisplayPlatformFunc = dlsym(dso, "ANGLEGetDisplayPlatform");
+ cnx->angleResetDisplayPlatformFunc = dlsym(dso, "ANGLEResetDisplayPlatform");
+
+ if (cnx->angleGetDisplayPlatformFunc) {
ALOGV("ANGLE GLES library loaded");
cnx->angleLoaded = true;
+ android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE);
} else {
ALOGV("Native GLES library loaded");
cnx->angleLoaded = false;
@@ -635,7 +663,13 @@
Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
const bool exact) {
ATRACE_CALL();
- android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL);
+ if (suffix && strcmp(suffix, "angle") == 0) {
+ // use system ANGLE driver
+ android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE);
+ } else {
+ android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL);
+ }
+
driver_t* hnd = nullptr;
void* dso = load_system_driver("GLES", suffix, exact);
if (dso) {
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index ee605c2..f0054a7 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -35,12 +35,6 @@
namespace angle {
-constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
-constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
-
-static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
-static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr;
-
static time_t startTime = time(nullptr);
static const unsigned char* getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/,
@@ -111,50 +105,19 @@
}
// Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace
-bool initializeAnglePlatform(EGLDisplay dpy) {
- // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform
- android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
- void* so = nullptr;
- if (ns) {
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = ns,
- };
- so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo);
- if (so) {
- ALOGD("dlopen_ext from APK (%s) success at %p", kAngleEs2Lib, so);
- } else {
- ALOGE("dlopen_ext(\"%s\") failed: %s", kAngleEs2Lib, dlerror());
- return false;
- }
- } else {
- // If we are here, ANGLE is loaded as built-in gl driver in the sphal.
- so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags);
- if (so) {
- ALOGD("dlopen (%s) success at %p", kAngleEs2Lib, so);
- } else {
- ALOGE("%s failed to dlopen %s: %s!", __FUNCTION__, kAngleEs2Lib, dlerror());
- return false;
- }
- }
-
- angleGetDisplayPlatform =
- reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform"));
-
- if (!angleGetDisplayPlatform) {
- ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!");
- dlclose(so);
+bool initializeAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx) {
+ if (cnx->angleGetDisplayPlatformFunc == nullptr) {
+ ALOGE("ANGLEGetDisplayPlatform is not initialized!");
return false;
}
- angleResetDisplayPlatform =
- reinterpret_cast<ResetDisplayPlatformFunc>(dlsym(so, "ANGLEResetDisplayPlatform"));
+ GetDisplayPlatformFunc angleGetDisplayPlatform =
+ reinterpret_cast<GetDisplayPlatformFunc>(cnx->angleGetDisplayPlatformFunc);
PlatformMethods* platformMethods = nullptr;
if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
&platformMethods))) {
ALOGE("ANGLEGetDisplayPlatform call failed!");
- dlclose(so);
return false;
}
if (platformMethods) {
@@ -166,8 +129,10 @@
return true;
}
-void resetAnglePlatform(EGLDisplay dpy) {
- if (angleResetDisplayPlatform) {
+void resetAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx) {
+ if (cnx->angleResetDisplayPlatformFunc) {
+ ResetDisplayPlatformFunc angleResetDisplayPlatform =
+ reinterpret_cast<ResetDisplayPlatformFunc>(cnx->angleResetDisplayPlatformFunc);
angleResetDisplayPlatform(dpy);
}
}
diff --git a/opengl/libs/EGL/egl_angle_platform.h b/opengl/libs/EGL/egl_angle_platform.h
index 6c24aa5..63806c2 100644
--- a/opengl/libs/EGL/egl_angle_platform.h
+++ b/opengl/libs/EGL/egl_angle_platform.h
@@ -25,8 +25,8 @@
namespace angle {
-bool initializeAnglePlatform(EGLDisplay dpy);
-void resetAnglePlatform(EGLDisplay dpy);
+bool initializeAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx);
+void resetAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx);
}; // namespace angle
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3317347..55a2682 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -168,7 +168,7 @@
if (dpy == EGL_NO_DISPLAY) {
ALOGE("eglGetPlatformDisplay failed!");
} else {
- if (!angle::initializeAnglePlatform(dpy)) {
+ if (!angle::initializeAnglePlatform(dpy, cnx)) {
ALOGE("initializeAnglePlatform failed!");
}
}
@@ -433,7 +433,7 @@
if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
// If we're using ANGLE reset any custom DisplayPlatform
if (cnx->angleLoaded) {
- angle::resetAnglePlatform(disp.dpy);
+ angle::resetAnglePlatform(disp.dpy, cnx);
}
if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 3bd37cb..90a3c19 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -42,7 +42,9 @@
libGles1(nullptr),
libGles2(nullptr),
systemDriverUnloaded(false),
- angleLoaded(false) {
+ angleLoaded(false),
+ angleGetDisplayPlatformFunc(nullptr),
+ angleResetDisplayPlatformFunc(nullptr) {
const char* const* entries = platform_names;
EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
while (*entries) {
@@ -75,6 +77,9 @@
bool systemDriverUnloaded;
bool angleLoaded; // Was ANGLE successfully loaded
+
+ void* angleGetDisplayPlatformFunc;
+ void* angleResetDisplayPlatformFunc;
};
extern gl_hooks_t gHooks[2];
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index f06a045..11b636d 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -163,11 +163,13 @@
addLoadingTime(driver, driverLoadingTime, &appInfo);
appInfo.appPackageName = appPackageName;
appInfo.driverVersionCode = driverVersionCode;
- appInfo.angleInUse = driverPackageName == "angle";
+ appInfo.angleInUse =
+ driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle";
appInfo.lastAccessTime = std::chrono::system_clock::now();
mAppStats.insert({appStatsKey, appInfo});
} else {
- mAppStats[appStatsKey].angleInUse = driverPackageName == "angle";
+ mAppStats[appStatsKey].angleInUse =
+ driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle";
addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now();
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 76729ef..b213f9a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -256,6 +256,7 @@
"inputflinger_input_reader_fuzzer",
"inputflinger_blocking_queue_fuzzer",
"inputflinger_input_classifier_fuzzer",
+ "inputflinger_input_dispatcher_fuzzer",
// Java/Kotlin targets
"CtsWindowManagerDeviceWindow",
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 1f17c16..016ae04 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -39,7 +39,7 @@
// Helper to std::visit with lambdas.
template <typename... V>
-struct Visitor : V... {};
+struct Visitor : V... { using V::operator()...; };
// explicit deduction guide (not needed as of C++20)
template <typename... V>
Visitor(V...) -> Visitor<V...>;
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index c34cd53..de836e9 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -190,7 +190,7 @@
// Helper to std::visit with lambdas.
template <typename... V>
-struct Visitor : V... {};
+struct Visitor : V... { using V::operator()...; };
// explicit deduction guide (not needed as of C++20)
template <typename... V>
Visitor(V...) -> Visitor<V...>;
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index e411abb..e529bdd 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -16,17 +16,51 @@
#define LOG_TAG "PointerChoreographer"
+#include <android-base/logging.h>
+#include <input/PrintTools.h>
+
#include "PointerChoreographer.h"
+#define INDENT " "
+
namespace android {
+namespace {
+bool isFromMouse(const NotifyMotionArgs& args) {
+ return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
+ args.pointerProperties[0].toolType == ToolType::MOUSE;
+}
+
+bool isHoverAction(int32_t action) {
+ return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
+}
+
+bool isStylusHoverEvent(const NotifyMotionArgs& args) {
+ return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
+}
+} // namespace
+
// --- PointerChoreographer ---
PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
PointerChoreographerPolicyInterface& policy)
- : mNextListener(listener) {}
+ : mTouchControllerConstructor([this]() REQUIRES(mLock) {
+ return mPolicy.createPointerController(
+ PointerControllerInterface::ControllerType::TOUCH);
+ }),
+ mNextListener(listener),
+ mPolicy(policy),
+ mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
+ mNotifiedPointerDisplayId(ADISPLAY_ID_NONE),
+ mShowTouchesEnabled(false),
+ mStylusPointerIconEnabled(false) {}
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ std::scoped_lock _l(mLock);
+
+ mInputDeviceInfos = args.inputDeviceInfos;
+ updatePointerControllersLocked();
mNextListener.notify(args);
}
@@ -39,7 +73,127 @@
}
void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
- mNextListener.notify(args);
+ NotifyMotionArgs newArgs = processMotion(args);
+
+ mNextListener.notify(newArgs);
+}
+
+NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
+ std::scoped_lock _l(mLock);
+
+ if (isFromMouse(args)) {
+ return processMouseEventLocked(args);
+ } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
+ processStylusHoverEventLocked(args);
+ } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
+ processTouchscreenAndStylusEventLocked(args);
+ }
+ return args;
+}
+
+NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
+ if (args.getPointerCount() != 1) {
+ LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
+ << args.dump();
+ }
+
+ const int32_t displayId = getTargetMouseDisplayLocked(args.displayId);
+
+ // Get the mouse pointer controller for the display, or create one if it doesn't exist.
+ auto [it, emplaced] =
+ mMousePointersByDisplay.try_emplace(displayId,
+ getMouseControllerConstructor(displayId));
+ if (emplaced) {
+ notifyPointerDisplayIdChangedLocked();
+ }
+
+ PointerControllerInterface& pc = *it->second;
+
+ const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ pc.move(deltaX, deltaY);
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ const auto [x, y] = pc.getPosition();
+ NotifyMotionArgs newArgs(args);
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ newArgs.xCursorPosition = x;
+ newArgs.yCursorPosition = y;
+ newArgs.displayId = displayId;
+ return newArgs;
+}
+
+/**
+ * When screen is touched, fade the mouse pointer on that display. We only call fade for
+ * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
+ * mouse device keeps moving and unfades the cursor.
+ * For touch events, we do not need to populate the cursor position.
+ */
+void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
+ if (args.displayId == ADISPLAY_ID_NONE) {
+ return;
+ }
+
+ if (const auto it = mMousePointersByDisplay.find(args.displayId);
+ it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
+ it->second->fade(PointerControllerInterface::Transition::GRADUAL);
+ }
+
+ if (!mShowTouchesEnabled) {
+ return;
+ }
+
+ // Get the touch pointer controller for the device, or create one if it doesn't exist.
+ auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
+
+ PointerControllerInterface& pc = *it->second;
+
+ const PointerCoords* coords = args.pointerCoords.data();
+ const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
+ const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
+ std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
+ BitSet32 idBits;
+ if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
+ continue;
+ }
+ uint32_t id = args.pointerProperties[i].id;
+ idToIndex[id] = i;
+ idBits.markBit(id);
+ }
+ }
+ // The PointerController already handles setting spots per-display, so
+ // we do not need to manually manage display changes for touch spots for now.
+ pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
+}
+
+void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
+ if (args.displayId == ADISPLAY_ID_NONE) {
+ return;
+ }
+
+ if (args.getPointerCount() != 1) {
+ LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
+ << args.dump();
+ }
+
+ // Get the stylus pointer controller for the device, or create one if it doesn't exist.
+ auto [it, _] =
+ mStylusPointersByDevice.try_emplace(args.deviceId,
+ getStylusControllerConstructor(args.displayId));
+
+ PointerControllerInterface& pc = *it->second;
+
+ const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+ pc.setPosition(x, y);
+ if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
+ } else {
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
}
void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
@@ -55,16 +209,222 @@
}
void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ processDeviceReset(args);
+
mNextListener.notify(args);
}
+void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
+ std::scoped_lock _l(mLock);
+ mTouchPointersByDevice.erase(args.deviceId);
+ mStylusPointersByDevice.erase(args.deviceId);
+}
+
void PointerChoreographer::notifyPointerCaptureChanged(
const NotifyPointerCaptureChangedArgs& args) {
+ if (args.request.enable) {
+ std::scoped_lock _l(mLock);
+ for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
+ mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+ }
mNextListener.notify(args);
}
void PointerChoreographer::dump(std::string& dump) {
+ std::scoped_lock _l(mLock);
+
dump += "PointerChoreographer:\n";
+ dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
+ dump += StringPrintf("stylus pointer icon enabled: %s\n",
+ mStylusPointerIconEnabled ? "true" : "false");
+
+ dump += INDENT "MousePointerControllers:\n";
+ for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
+ std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
+ dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
+ }
+ dump += INDENT "TouchPointerControllers:\n";
+ for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
+ std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
+ dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
+ }
+ dump += INDENT "StylusPointerControllers:\n";
+ for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
+ std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
+ dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
+ }
+ dump += "\n";
+}
+
+const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const {
+ for (auto& viewport : mViewports) {
+ if (viewport.displayId == displayId) {
+ return &viewport;
+ }
+ }
+ return nullptr;
+}
+
+int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const {
+ return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
+}
+
+InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
+ auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
+ [deviceId](const auto& info) { return info.getId() == deviceId; });
+ return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
+}
+
+void PointerChoreographer::updatePointerControllersLocked() {
+ std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
+ std::set<DeviceId> touchDevicesToKeep;
+ std::set<DeviceId> stylusDevicesToKeep;
+
+ // Mark the displayIds or deviceIds of PointerControllers currently needed.
+ for (const auto& info : mInputDeviceInfos) {
+ const uint32_t sources = info.getSources();
+ if (isFromSource(sources, AINPUT_SOURCE_MOUSE) ||
+ isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) {
+ const int32_t resolvedDisplayId =
+ getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
+ mouseDisplaysToKeep.insert(resolvedDisplayId);
+ }
+ if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
+ info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+ touchDevicesToKeep.insert(info.getId());
+ }
+ if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
+ info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+ stylusDevicesToKeep.insert(info.getId());
+ }
+ }
+
+ // Remove PointerControllers no longer needed.
+ std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
+ return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
+ });
+ std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
+ return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
+ });
+ std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
+ return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
+ });
+
+ // Notify the policy if there's a change on the pointer display ID.
+ notifyPointerDisplayIdChangedLocked();
+}
+
+void PointerChoreographer::notifyPointerDisplayIdChangedLocked() {
+ int32_t displayIdToNotify = ADISPLAY_ID_NONE;
+ FloatPoint cursorPosition = {0, 0};
+ if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
+ it != mMousePointersByDisplay.end()) {
+ const auto& pointerController = it->second;
+ // Use the displayId from the pointerController, because it accurately reflects whether
+ // the viewport has been added for that display. Otherwise, we would have to check if
+ // the viewport exists separately.
+ displayIdToNotify = pointerController->getDisplayId();
+ cursorPosition = pointerController->getPosition();
+ }
+
+ if (mNotifiedPointerDisplayId == displayIdToNotify) {
+ return;
+ }
+ mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition);
+ mNotifiedPointerDisplayId = displayIdToNotify;
+}
+
+void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
+ std::scoped_lock _l(mLock);
+
+ mDefaultMouseDisplayId = displayId;
+ updatePointerControllersLocked();
+}
+
+void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
+ std::scoped_lock _l(mLock);
+ for (const auto& viewport : viewports) {
+ const int32_t displayId = viewport.displayId;
+ if (const auto it = mMousePointersByDisplay.find(displayId);
+ it != mMousePointersByDisplay.end()) {
+ it->second->setDisplayViewport(viewport);
+ }
+ for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
+ const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
+ if (info && info->getAssociatedDisplayId() == displayId) {
+ stylusPointerController->setDisplayViewport(viewport);
+ }
+ }
+ }
+ mViewports = viewports;
+ notifyPointerDisplayIdChangedLocked();
+}
+
+std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
+ int32_t associatedDisplayId) {
+ std::scoped_lock _l(mLock);
+ const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
+ if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
+ return *viewport;
+ }
+ return std::nullopt;
+}
+
+FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) {
+ std::scoped_lock _l(mLock);
+ const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
+ if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
+ it != mMousePointersByDisplay.end()) {
+ return it->second->getPosition();
+ }
+ return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
+}
+
+void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
+ std::scoped_lock _l(mLock);
+ if (mShowTouchesEnabled == enabled) {
+ return;
+ }
+ mShowTouchesEnabled = enabled;
+ updatePointerControllersLocked();
+}
+
+void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
+ std::scoped_lock _l(mLock);
+ if (mStylusPointerIconEnabled == enabled) {
+ return;
+ }
+ mStylusPointerIconEnabled = enabled;
+ updatePointerControllersLocked();
+}
+
+PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
+ int32_t displayId) {
+ std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
+ [this, displayId]() REQUIRES(mLock) {
+ auto pc = mPolicy.createPointerController(
+ PointerControllerInterface::ControllerType::MOUSE);
+ if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
+ pc->setDisplayViewport(*viewport);
+ }
+ return pc;
+ };
+ return ConstructorDelegate(std::move(ctor));
+}
+
+PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
+ int32_t displayId) {
+ std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
+ [this, displayId]() REQUIRES(mLock) {
+ auto pc = mPolicy.createPointerController(
+ PointerControllerInterface::ControllerType::STYLUS);
+ if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
+ pc->setDisplayViewport(*viewport);
+ }
+ return pc;
+ };
+ return ConstructorDelegate(std::move(ctor));
}
} // namespace android
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 5e5f782..26d2fef 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -20,9 +20,26 @@
#include "NotifyArgs.h"
#include "PointerChoreographerPolicyInterface.h"
+#include <android-base/thread_annotations.h>
+#include <type_traits>
+
namespace android {
/**
+ * A helper class that wraps a factory method that acts as a constructor for the type returned
+ * by the factory method.
+ */
+template <typename Factory>
+struct ConstructorDelegate {
+ constexpr ConstructorDelegate(Factory&& factory) : mFactory(std::move(factory)) {}
+
+ using ConstructedType = std::invoke_result_t<const Factory&>;
+ constexpr operator ConstructedType() const { return mFactory(); }
+
+ Factory mFactory;
+};
+
+/**
* PointerChoreographer manages the icons shown by the system for input interactions.
* This includes showing the mouse cursor, stylus hover icons, and touch spots.
* It is responsible for accumulating the location of the mouse cursor, and populating
@@ -31,6 +48,17 @@
class PointerChoreographerInterface : public InputListenerInterface {
public:
/**
+ * Set the display that pointers, like the mouse cursor and drawing tablets,
+ * should be drawn on.
+ */
+ virtual void setDefaultMouseDisplayId(int32_t displayId) = 0;
+ virtual void setDisplayViewports(const std::vector<DisplayViewport>& viewports) = 0;
+ virtual std::optional<DisplayViewport> getViewportForPointerDevice(
+ int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
+ virtual FloatPoint getMouseCursorPosition(int32_t displayId) = 0;
+ virtual void setShowTouchesEnabled(bool enabled) = 0;
+ virtual void setStylusPointerIconEnabled(bool enabled) = 0;
+ /**
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
virtual void dump(std::string& dump) = 0;
@@ -42,6 +70,14 @@
PointerChoreographerPolicyInterface&);
~PointerChoreographer() override = default;
+ void setDefaultMouseDisplayId(int32_t displayId) override;
+ void setDisplayViewports(const std::vector<DisplayViewport>& viewports) override;
+ std::optional<DisplayViewport> getViewportForPointerDevice(
+ int32_t associatedDisplayId) override;
+ FloatPoint getMouseCursorPosition(int32_t displayId) override;
+ void setShowTouchesEnabled(bool enabled) override;
+ void setStylusPointerIconEnabled(bool enabled) override;
+
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
@@ -55,7 +91,42 @@
void dump(std::string& dump) override;
private:
+ void updatePointerControllersLocked() REQUIRES(mLock);
+ void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock);
+ const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
+ int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);
+ InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
+
+ NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
+ NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ void processDeviceReset(const NotifyDeviceResetArgs& args);
+
+ using ControllerConstructor =
+ ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
+ ControllerConstructor mTouchControllerConstructor GUARDED_BY(mLock);
+ ControllerConstructor getMouseControllerConstructor(int32_t displayId) REQUIRES(mLock);
+ ControllerConstructor getStylusControllerConstructor(int32_t displayId) REQUIRES(mLock);
+
+ std::mutex mLock;
+
InputListenerInterface& mNextListener;
+ PointerChoreographerPolicyInterface& mPolicy;
+
+ std::map<int32_t, std::shared_ptr<PointerControllerInterface>> mMousePointersByDisplay
+ GUARDED_BY(mLock);
+ std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mTouchPointersByDevice
+ GUARDED_BY(mLock);
+ std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice
+ GUARDED_BY(mLock);
+
+ int32_t mDefaultMouseDisplayId GUARDED_BY(mLock);
+ int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock);
+ std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
+ std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
+ bool mShowTouchesEnabled GUARDED_BY(mLock);
+ bool mStylusPointerIconEnabled GUARDED_BY(mLock);
};
} // namespace android
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
index ee0ab33..d9d0450 100644
--- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -15,10 +15,15 @@
*/
#include "PreferStylusOverTouchBlocker.h"
+#include <com_android_input_flags.h>
#include <input/PrintTools.h>
+namespace input_flags = com::android::input::flags;
+
namespace android {
+const bool BLOCK_TOUCH_WHEN_STYLUS_HOVER = !input_flags::disable_reject_touch_on_stylus_hover();
+
static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) {
bool hasStylus = false;
bool hasTouch = false;
@@ -96,8 +101,11 @@
std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion(
const NotifyMotionArgs& args) {
const auto [hasTouch, hasStylus] = checkToolType(args);
- const bool isUpOrCancel =
- args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL;
+ const bool isDisengageOrCancel = BLOCK_TOUCH_WHEN_STYLUS_HOVER
+ ? (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL)
+ : (args.action == AMOTION_EVENT_ACTION_UP ||
+ args.action == AMOTION_EVENT_ACTION_CANCEL);
if (hasTouch && hasStylus) {
mDevicesWithMixedToolType.insert(args.deviceId);
@@ -109,7 +117,7 @@
if (mCanceledDevices.find(args.deviceId) != mCanceledDevices.end()) {
// If we started to cancel events from this device, continue to do so to keep
// the stream consistent. It should happen at most once per "mixed" device.
- if (isUpOrCancel) {
+ if (isDisengageOrCancel) {
mCanceledDevices.erase(args.deviceId);
mLastTouchEvents.erase(args.deviceId);
}
@@ -119,10 +127,13 @@
}
const bool isStylusEvent = hasStylus;
- const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
+ const bool isEngage = BLOCK_TOUCH_WHEN_STYLUS_HOVER
+ ? (args.action == AMOTION_EVENT_ACTION_DOWN ||
+ args.action == AMOTION_EVENT_ACTION_HOVER_ENTER)
+ : (args.action == AMOTION_EVENT_ACTION_DOWN);
if (isStylusEvent) {
- if (isDown) {
+ if (isEngage) {
// Reject all touch while stylus is down
mActiveStyli.insert(args.deviceId);
@@ -143,7 +154,7 @@
result.push_back(args);
return result;
}
- if (isUpOrCancel) {
+ if (isDisengageOrCancel) {
mActiveStyli.erase(args.deviceId);
}
// Never drop stylus events
@@ -158,7 +169,7 @@
}
const bool shouldDrop = mCanceledDevices.find(args.deviceId) != mCanceledDevices.end();
- if (isUpOrCancel) {
+ if (isDisengageOrCancel) {
mCanceledDevices.erase(args.deviceId);
mLastTouchEvents.erase(args.deviceId);
}
@@ -169,7 +180,7 @@
return {};
}
- if (!isUpOrCancel) {
+ if (!isDisengageOrCancel) {
mLastTouchEvents[args.deviceId] = args;
}
return {args};
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 6f092a6..513cbfa 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -148,7 +148,7 @@
]
}
],
- "hwasan-postsubmit": [
+ "postsubmit": [
{
"name": "CtsWindowManagerDeviceWindow",
"options": [
@@ -281,6 +281,9 @@
"include-filter": "android.security.cts.Poc19_03#testPocBug_115739809"
}
]
+ },
+ {
+ "name": "CtsInputHostTestCases"
}
]
}
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 0f62324..1e2b9b3a 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -67,6 +67,16 @@
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 @@
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 188d5f0..5ae3715 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -20,10 +20,12 @@
#include <binder/Binder.h>
#include <gui/constants.h>
#include "../dispatcher/InputDispatcher.h"
+#include "../tests/FakeApplicationHandle.h"
+#include "../tests/FakeInputDispatcherPolicy.h"
+#include "../tests/FakeWindowHandle.h"
using android::base::Result;
using android::gui::WindowInfo;
-using android::gui::WindowInfoHandle;
using android::os::IInputConstants;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
@@ -33,171 +35,17 @@
namespace {
// An arbitrary device id.
-constexpr int32_t DEVICE_ID = 1;
+constexpr DeviceId DEVICE_ID = 1;
-// The default pid and uid for windows created by the test.
-constexpr gui::Pid WINDOW_PID{999};
-constexpr gui::Uid WINDOW_UID{1001};
+// An arbitrary display id
+constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
-static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
static nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-// --- FakeInputDispatcherPolicy ---
-
-class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
-public:
- FakeInputDispatcherPolicy() = default;
- virtual ~FakeInputDispatcherPolicy() = default;
-
-private:
- void notifyConfigurationChanged(nsecs_t) override {}
-
- void notifyNoFocusedWindowAnr(
- const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
- ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
- }
-
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
- const std::string& reason) override {
- ALOGE("Window is not responding: %s", reason.c_str());
- }
-
- void notifyWindowResponsive(const sp<IBinder>& connectionToken,
- std::optional<gui::Pid> pid) override {}
-
- void notifyInputChannelBroken(const sp<IBinder>&) override {}
-
- void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
-
- void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
- InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
- const std::vector<float>& values) override {}
-
- void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
- InputDeviceSensorAccuracy accuracy) override {}
-
- void notifyVibratorState(int32_t deviceId, bool isOn) override {}
-
- bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
- return true; // dispatch event normally
- }
-
- void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}
-
- void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
-
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
- return 0;
- }
-
- std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
- uint32_t) override {
- return {};
- }
-
- void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
-
- void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
-
- void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
-
- void setPointerCapture(const PointerCaptureRequest&) override {}
-
- void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
-
- void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<gui::Uid>& uids) override {}
-
- InputDispatcherConfiguration mConfig;
-};
-
-class FakeApplicationHandle : public InputApplicationHandle {
-public:
- FakeApplicationHandle() {}
- virtual ~FakeApplicationHandle() {}
-
- virtual bool updateInfo() {
- mInfo.dispatchingTimeoutMillis =
- std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
- return true;
- }
-};
-
-class FakeInputReceiver {
-public:
- void consumeEvent() {
- uint32_t consumeSeq = 0;
- InputEvent* event;
-
- std::chrono::time_point start = std::chrono::steady_clock::now();
- status_t result = WOULD_BLOCK;
- while (result == WOULD_BLOCK) {
- std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
- if (elapsed > 10ms) {
- ALOGE("Waited too long for consumer to produce an event, giving up");
- break;
- }
- result = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
- &event);
- }
- if (result != OK) {
- ALOGE("Received result = %d from consume()", result);
- }
- result = mConsumer->sendFinishedSignal(consumeSeq, true);
- if (result != OK) {
- ALOGE("Received result = %d from sendFinishedSignal", result);
- }
- }
-
-protected:
- explicit FakeInputReceiver(InputDispatcher& dispatcher, const std::string name) {
- Result<std::unique_ptr<InputChannel>> channelResult = dispatcher.createInputChannel(name);
- LOG_ALWAYS_FATAL_IF(!channelResult.ok());
- mClientChannel = std::move(*channelResult);
- mConsumer = std::make_unique<InputConsumer>(mClientChannel);
- }
-
- virtual ~FakeInputReceiver() {}
-
- std::shared_ptr<InputChannel> mClientChannel;
- std::unique_ptr<InputConsumer> mConsumer;
- PreallocatedInputEventFactory mEventFactory;
-};
-
-class FakeWindowHandle : public WindowInfoHandle, public FakeInputReceiver {
-public:
- static const int32_t WIDTH = 200;
- static const int32_t HEIGHT = 200;
-
- FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- InputDispatcher& dispatcher, const std::string name)
- : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
- inputApplicationHandle->updateInfo();
- updateInfo();
- mInfo.applicationInfo = *inputApplicationHandle->getInfo();
- }
-
- void updateInfo() {
- mInfo.token = mClientChannel->getConnectionToken();
- mInfo.name = "FakeWindowHandle";
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.frame = mFrame;
- mInfo.globalScaleFactor = 1.0;
- mInfo.touchableRegion.clear();
- mInfo.addTouchableRegion(mFrame);
- mInfo.ownerPid = WINDOW_PID;
- mInfo.ownerUid = WINDOW_UID;
- mInfo.displayId = ADISPLAY_ID_DEFAULT;
- }
-
-protected:
- Rect mFrame;
-};
-
static MotionEvent generateMotionEvent() {
PointerProperties pointerProperties[1];
PointerCoords pointerCoords[1];
@@ -263,7 +111,7 @@
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
+ sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);
dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -281,8 +129,8 @@
motionArgs.eventTime = now();
dispatcher.notifyMotion(motionArgs);
- window->consumeEvent();
- window->consumeEvent();
+ window->consumeMotion();
+ window->consumeMotion();
}
dispatcher.stop();
@@ -298,7 +146,7 @@
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
+ sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);
dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -315,8 +163,8 @@
INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
- window->consumeEvent();
- window->consumeEvent();
+ window->consumeMotion();
+ window->consumeMotion();
}
dispatcher.stop();
@@ -332,7 +180,7 @@
// Create a window
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
+ sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);
std::vector<gui::WindowInfo> windowInfos{*window->getInfo()};
gui::DisplayInfo info;
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index c7d98ab..c889b9b 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -69,6 +69,16 @@
android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Injection");
/**
+ * Generally, we always log whenever events are dropped. However, to reduce logspam, some messages
+ * are suppressed.
+ * Log additional debug messages about dropped input events with this flag.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherDroppedEventsVerbose DEBUG".
+ * Requires system_server restart via `adb shell stop && adb shell start`.
+ */
+const bool DEBUG_DROPPED_EVENTS_VERBOSE =
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "DroppedEventsVerbose");
+
+/**
* Log debug messages about input focus tracking.
* Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart)
*/
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 30e6802..2d1a22b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -67,24 +67,11 @@
injectionState(nullptr),
dispatchInProgress(false) {}
-EventEntry::~EventEntry() {
- releaseInjectionState();
-}
-
-void EventEntry::releaseInjectionState() {
- if (injectionState) {
- injectionState->release();
- injectionState = nullptr;
- }
-}
-
// --- ConfigurationChangedEntry ---
ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime)
: EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
-ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
-
std::string ConfigurationChangedEntry::getDescription() const {
return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
}
@@ -94,8 +81,6 @@
DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId)
: EventEntry(id, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
-DeviceResetEntry::~DeviceResetEntry() {}
-
std::string DeviceResetEntry::getDescription() const {
return StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
}
@@ -110,8 +95,6 @@
hasFocus(hasFocus),
reason(reason) {}
-FocusEntry::~FocusEntry() {}
-
std::string FocusEntry::getDescription() const {
return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
}
@@ -125,8 +108,6 @@
: EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
pointerCaptureRequest(request) {}
-PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
-
std::string PointerCaptureChangedEntry::getDescription() const {
return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
pointerCaptureRequest.enable ? "true" : "false");
@@ -143,18 +124,16 @@
x(x),
y(y) {}
-DragEntry::~DragEntry() {}
-
std::string DragEntry::getDescription() const {
return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
}
// --- KeyEntry ---
-KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
- nsecs_t downTime)
+KeyEntry::KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, int32_t repeatCount, nsecs_t downTime)
: EventEntry(id, Type::KEY, eventTime, policyFlags),
deviceId(deviceId),
source(source),
@@ -168,9 +147,9 @@
downTime(downTime),
syntheticRepeat(false),
interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN),
- interceptKeyWakeupTime(0) {}
-
-KeyEntry::~KeyEntry() {}
+ interceptKeyWakeupTime(0) {
+ EventEntry::injectionState = std::move(injectionState);
+}
std::string KeyEntry::getDescription() const {
if (!IS_DEBUGGABLE_BUILD) {
@@ -185,15 +164,6 @@
keyCode, scanCode, metaState, repeatCount, policyFlags);
}
-void KeyEntry::recycle() {
- releaseInjectionState();
-
- dispatchInProgress = false;
- syntheticRepeat = false;
- interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
- interceptKeyWakeupTime = 0;
-}
-
// --- TouchModeEntry ---
TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId)
@@ -201,21 +171,19 @@
inTouchMode(inTouchMode),
displayId(displayId) {}
-TouchModeEntry::~TouchModeEntry() {}
-
std::string TouchModeEntry::getDescription() const {
return StringPrintf("TouchModeEvent(inTouchMode=%s)", inTouchMode ? "true" : "false");
}
// --- MotionEntry ---
-MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState,
- int32_t buttonState, MotionClassification classification,
- int32_t edgeFlags, float xPrecision, float yPrecision,
- float xCursorPosition, float yCursorPosition, nsecs_t downTime,
- const std::vector<PointerProperties>& pointerProperties,
+MotionEntry::MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState,
+ nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+ uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
+ int32_t metaState, int32_t buttonState,
+ MotionClassification classification, int32_t edgeFlags, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition,
+ nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties,
const std::vector<PointerCoords>& pointerCoords)
: EventEntry(id, Type::MOTION, eventTime, policyFlags),
deviceId(deviceId),
@@ -234,9 +202,9 @@
yCursorPosition(yCursorPosition),
downTime(downTime),
pointerProperties(pointerProperties),
- pointerCoords(pointerCoords) {}
-
-MotionEntry::~MotionEntry() {}
+ pointerCoords(pointerCoords) {
+ EventEntry::injectionState = std::move(injectionState);
+}
std::string MotionEntry::getDescription() const {
if (!IS_DEBUGGABLE_BUILD) {
@@ -285,8 +253,6 @@
hwTimestamp(hwTimestamp),
values(std::move(values)) {}
-SensorEntry::~SensorEntry() {}
-
std::string SensorEntry::getDescription() const {
std::string msg;
msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, "
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index b341784..d44a211 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -48,7 +48,7 @@
Type type;
nsecs_t eventTime;
uint32_t policyFlags;
- InjectionState* injectionState;
+ std::shared_ptr<InjectionState> injectionState;
bool dispatchInProgress; // initially false, set to true while dispatching
@@ -72,17 +72,12 @@
virtual std::string getDescription() const = 0;
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
- virtual ~EventEntry();
-
-protected:
- void releaseInjectionState();
+ virtual ~EventEntry() = default;
};
struct ConfigurationChangedEntry : EventEntry {
explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
std::string getDescription() const override;
-
- ~ConfigurationChangedEntry() override;
};
struct DeviceResetEntry : EventEntry {
@@ -90,8 +85,6 @@
DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
std::string getDescription() const override;
-
- ~DeviceResetEntry() override;
};
struct FocusEntry : EventEntry {
@@ -102,8 +95,6 @@
FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
const std::string& reason);
std::string getDescription() const override;
-
- ~FocusEntry() override;
};
struct PointerCaptureChangedEntry : EventEntry {
@@ -111,8 +102,6 @@
PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
std::string getDescription() const override;
-
- ~PointerCaptureChangedEntry() override;
};
struct DragEntry : EventEntry {
@@ -123,8 +112,6 @@
DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
float y);
std::string getDescription() const override;
-
- ~DragEntry() override;
};
struct KeyEntry : EventEntry {
@@ -150,13 +137,11 @@
InterceptKeyResult interceptKeyResult; // set based on the interception result
nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
- KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
- int32_t metaState, int32_t repeatCount, nsecs_t downTime);
+ KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+ int32_t repeatCount, nsecs_t downTime);
std::string getDescription() const override;
- void recycle();
-
- ~KeyEntry() override;
};
struct MotionEntry : EventEntry {
@@ -180,16 +165,14 @@
size_t getPointerCount() const { return pointerProperties.size(); }
- MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
- int32_t metaState, int32_t buttonState, MotionClassification classification,
- int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime,
- const std::vector<PointerProperties>& pointerProperties,
+ MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, int32_t edgeFlags,
+ float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition,
+ nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties,
const std::vector<PointerCoords>& pointerCoords);
std::string getDescription() const override;
-
- ~MotionEntry() override;
};
std::ostream& operator<<(std::ostream& out, const MotionEntry& motionEntry);
@@ -209,8 +192,6 @@
InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
std::vector<float> values);
std::string getDescription() const override;
-
- ~SensorEntry() override;
};
struct TouchModeEntry : EventEntry {
@@ -219,8 +200,6 @@
TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int32_t displayId);
std::string getDescription() const override;
-
- ~TouchModeEntry() override;
};
// Tracks the progress of dispatching a particular event to a particular connection.
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index 053594b..f693dcf 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -20,22 +20,10 @@
namespace android::inputdispatcher {
-InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid)
- : refCount(1),
- targetUid(targetUid),
+InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid, bool isAsync)
+ : targetUid(targetUid),
+ injectionIsAsync(isAsync),
injectionResult(android::os::InputEventInjectionResult::PENDING),
- injectionIsAsync(false),
pendingForegroundDispatches(0) {}
-InjectionState::~InjectionState() {}
-
-void InjectionState::release() {
- refCount -= 1;
- if (refCount == 0) {
- delete this;
- } else {
- ALOG_ASSERT(refCount > 0);
- }
-}
-
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 3a3f5ae..8225dec 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -24,18 +24,12 @@
namespace inputdispatcher {
struct InjectionState {
- mutable int32_t refCount;
-
- std::optional<gui::Uid> targetUid;
+ const std::optional<gui::Uid> targetUid;
+ const bool injectionIsAsync; // set to true if injection is not waiting for the result
android::os::InputEventInjectionResult injectionResult; // initially PENDING
- bool injectionIsAsync; // set to true if injection is not waiting for the result
int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
- explicit InjectionState(const std::optional<gui::Uid>& targetUid);
- void release();
-
-private:
- ~InjectionState();
+ explicit InjectionState(const std::optional<gui::Uid>& targetUid, bool isAsync);
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7dfbf94..1958c35 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -72,6 +72,8 @@
using android::os::InputEventInjectionSync;
namespace input_flags = com::android::input::flags;
+static const bool REMOVE_APP_SWITCH_DROPS = input_flags::remove_app_switch_drops();
+
namespace android::inputdispatcher {
namespace {
@@ -390,21 +392,17 @@
}
std::unique_ptr<MotionEntry> combinedMotionEntry =
- std::make_unique<MotionEntry>(motionEntry.id, motionEntry.eventTime,
- motionEntry.deviceId, motionEntry.source,
- motionEntry.displayId, motionEntry.policyFlags,
- motionEntry.action, motionEntry.actionButton,
- motionEntry.flags, motionEntry.metaState,
- motionEntry.buttonState, motionEntry.classification,
- motionEntry.edgeFlags, motionEntry.xPrecision,
- motionEntry.yPrecision, motionEntry.xCursorPosition,
- motionEntry.yCursorPosition, motionEntry.downTime,
- motionEntry.pointerProperties, pointerCoords);
-
- if (motionEntry.injectionState) {
- combinedMotionEntry->injectionState = motionEntry.injectionState;
- combinedMotionEntry->injectionState->refCount += 1;
- }
+ std::make_unique<MotionEntry>(motionEntry.id, motionEntry.injectionState,
+ motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId,
+ motionEntry.policyFlags, motionEntry.action,
+ motionEntry.actionButton, motionEntry.flags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime, motionEntry.pointerProperties,
+ pointerCoords);
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
@@ -756,10 +754,6 @@
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
- : InputDispatcher(policy, STALE_EVENT_TIMEOUT) {}
-
-InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
- std::chrono::nanoseconds staleEventTimeout)
: mPolicy(policy),
mPendingEvent(nullptr),
mLastDropReason(DropReason::NOT_DROPPED),
@@ -774,7 +768,6 @@
mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
- mStaleEventTimeout(staleEventTimeout),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator) {
mLooper = sp<Looper>::make(false);
@@ -956,20 +949,25 @@
// Optimize latency of app switches.
// Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
// been pressed. When it expires, we preempt dispatch and drop all other pending events.
- bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
- if (mAppSwitchDueTime < *nextWakeupTime) {
- *nextWakeupTime = mAppSwitchDueTime;
+ bool isAppSwitchDue;
+ if (!REMOVE_APP_SWITCH_DROPS) {
+ isAppSwitchDue = mAppSwitchDueTime <= currentTime;
+ if (mAppSwitchDueTime < *nextWakeupTime) {
+ *nextWakeupTime = mAppSwitchDueTime;
+ }
}
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (!mPendingEvent) {
if (mInboundQueue.empty()) {
- if (isAppSwitchDue) {
- // The inbound queue is empty so the app switch key we were waiting
- // for will never arrive. Stop waiting for it.
- resetPendingAppSwitchLocked(false);
- isAppSwitchDue = false;
+ if (!REMOVE_APP_SWITCH_DROPS) {
+ if (isAppSwitchDue) {
+ // The inbound queue is empty so the app switch key we were waiting
+ // for will never arrive. Stop waiting for it.
+ resetPendingAppSwitchLocked(false);
+ isAppSwitchDue = false;
+ }
}
// Synthesize a key repeat if appropriate.
@@ -1067,12 +1065,14 @@
case EventEntry::Type::KEY: {
std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
- if (isAppSwitchDue) {
- if (isAppSwitchKeyEvent(*keyEntry)) {
- resetPendingAppSwitchLocked(true);
- isAppSwitchDue = false;
- } else if (dropReason == DropReason::NOT_DROPPED) {
- dropReason = DropReason::APP_SWITCH;
+ if (!REMOVE_APP_SWITCH_DROPS) {
+ if (isAppSwitchDue) {
+ if (isAppSwitchKeyEvent(*keyEntry)) {
+ resetPendingAppSwitchLocked(true);
+ isAppSwitchDue = false;
+ } else if (dropReason == DropReason::NOT_DROPPED) {
+ dropReason = DropReason::APP_SWITCH;
+ }
}
}
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
@@ -1088,8 +1088,10 @@
case EventEntry::Type::MOTION: {
std::shared_ptr<MotionEntry> motionEntry =
std::static_pointer_cast<MotionEntry>(mPendingEvent);
- if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
- dropReason = DropReason::APP_SWITCH;
+ if (!REMOVE_APP_SWITCH_DROPS) {
+ if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
+ dropReason = DropReason::APP_SWITCH;
+ }
}
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
dropReason = DropReason::STALE;
@@ -1104,8 +1106,10 @@
case EventEntry::Type::SENSOR: {
std::shared_ptr<SensorEntry> sensorEntry =
std::static_pointer_cast<SensorEntry>(mPendingEvent);
- if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
- dropReason = DropReason::APP_SWITCH;
+ if (!REMOVE_APP_SWITCH_DROPS) {
+ if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
+ dropReason = DropReason::APP_SWITCH;
+ }
}
// Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use
// 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead.
@@ -1131,7 +1135,7 @@
}
bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
- return std::chrono::nanoseconds(currentTime - entry.eventTime) >= mStaleEventTimeout;
+ return mPolicy.isStaleEvent(currentTime, entry.eventTime);
}
/**
@@ -1207,21 +1211,23 @@
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
- if (isAppSwitchKeyEvent(keyEntry)) {
- if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
- mAppSwitchSawKeyDown = true;
- } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
- if (mAppSwitchSawKeyDown) {
- if (DEBUG_APP_SWITCH) {
- ALOGD("App switch is pending!");
+
+ if (!REMOVE_APP_SWITCH_DROPS) {
+ if (isAppSwitchKeyEvent(keyEntry)) {
+ if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
+ mAppSwitchSawKeyDown = true;
+ } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
+ if (mAppSwitchSawKeyDown) {
+ if (DEBUG_APP_SWITCH) {
+ ALOGD("App switch is pending!");
+ }
+ mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
+ mAppSwitchSawKeyDown = false;
+ needWake = true;
}
- mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
- mAppSwitchSawKeyDown = false;
- needWake = true;
}
}
}
-
// If a new up event comes in, and the pending event with same key code has been asked
// to try again later because of the policy. We have to reset the intercept key wake up
// time for it may have been handled in the policy and could be dropped.
@@ -1482,7 +1488,7 @@
}
void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
- InjectionState* injectionState = entry->injectionState;
+ const std::shared_ptr<InjectionState>& injectionState = entry->injectionState;
if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("Injected inbound event was dropped.");
@@ -1508,10 +1514,11 @@
(POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
std::shared_ptr<KeyEntry> newEntry =
- std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, entry->deviceId,
- entry->source, entry->displayId, policyFlags, entry->action,
- entry->flags, entry->keyCode, entry->scanCode,
- entry->metaState, entry->repeatCount + 1, entry->downTime);
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, entry->deviceId, entry->source,
+ entry->displayId, policyFlags, entry->action, entry->flags,
+ entry->keyCode, entry->scanCode, entry->metaState,
+ entry->repeatCount + 1, entry->downTime);
newEntry->syntheticRepeat = true;
mKeyRepeatState.lastKeyEntry = newEntry;
@@ -2032,10 +2039,10 @@
if (connection != nullptr) {
prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
} else {
- if (DEBUG_FOCUS) {
- ALOGD("Dropping event delivery to target with channel '%s' because it "
- "is no longer registered with the input dispatcher.",
- inputTarget.inputChannel->getName().c_str());
+ if (DEBUG_DROPPED_EVENTS_VERBOSE) {
+ LOG(INFO) << "Dropping event delivery to target with channel "
+ << inputTarget.inputChannel->getName()
+ << " because it is no longer registered with the input dispatcher.";
}
}
}
@@ -2453,10 +2460,11 @@
// If the pointer is not currently down, then ignore the event.
if (!tempTouchState.isDown(entry.deviceId) &&
maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
- LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId
- << " is not down or we previously "
- "dropped the pointer down event in display "
- << displayId << ": " << entry.getDescription();
+ if (DEBUG_DROPPED_EVENTS_VERBOSE) {
+ LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId
+ << " is not down or we previously dropped the pointer down event in "
+ << "display " << displayId << ": " << entry.getDescription();
+ }
outInjectionResult = InputEventInjectionResult::FAILED;
return {};
}
@@ -2864,8 +2872,13 @@
it = inputTargets.end() - 1;
}
- LOG_ALWAYS_FATAL_IF(it->flags != targetFlags);
- LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
+ if (it->flags != targetFlags) {
+ LOG(ERROR) << "Flags don't match! targetFlags=" << targetFlags.string() << ", it=" << *it;
+ }
+ if (it->globalScaleFactor != windowInfo->globalScaleFactor) {
+ LOG(ERROR) << "Mismatch! it->globalScaleFactor=" << it->globalScaleFactor
+ << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor;
+ }
}
void InputDispatcher::addPointerWindowTargetLocked(
@@ -2910,8 +2923,13 @@
it = inputTargets.end() - 1;
}
- LOG_ALWAYS_FATAL_IF(it->flags != targetFlags);
- LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
+ if (it->flags != targetFlags) {
+ LOG(ERROR) << "Flags don't match! targetFlags=" << targetFlags.string() << ", it=" << *it;
+ }
+ if (it->globalScaleFactor != windowInfo->globalScaleFactor) {
+ LOG(ERROR) << "Mismatch! it->globalScaleFactor=" << it->globalScaleFactor
+ << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor;
+ }
it->addPointers(pointerIds, windowInfo->transform);
}
@@ -4226,7 +4244,8 @@
").",
originalMotionEntry.id, newId));
std::unique_ptr<MotionEntry> splitMotionEntry =
- std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime,
+ std::make_unique<MotionEntry>(newId, originalMotionEntry.injectionState,
+ originalMotionEntry.eventTime,
originalMotionEntry.deviceId, originalMotionEntry.source,
originalMotionEntry.displayId,
originalMotionEntry.policyFlags, action,
@@ -4241,11 +4260,6 @@
originalMotionEntry.yCursorPosition, splitDownTime,
splitPointerProperties, splitPointerCoords);
- if (originalMotionEntry.injectionState) {
- splitMotionEntry->injectionState = originalMotionEntry.injectionState;
- splitMotionEntry->injectionState->refCount += 1;
- }
-
return splitMotionEntry;
}
@@ -4333,9 +4347,10 @@
}
std::unique_ptr<KeyEntry> newEntry =
- std::make_unique<KeyEntry>(args.id, args.eventTime, args.deviceId, args.source,
- args.displayId, policyFlags, args.action, flags, keyCode,
- args.scanCode, metaState, repeatCount, args.downTime);
+ std::make_unique<KeyEntry>(args.id, /*injectionState=*/nullptr, args.eventTime,
+ args.deviceId, args.source, args.displayId, policyFlags,
+ args.action, flags, keyCode, args.scanCode, metaState,
+ repeatCount, args.downTime);
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
@@ -4419,7 +4434,8 @@
const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);
if (touchStateIt != mTouchStatesByDisplay.end()) {
const TouchState& touchState = touchStateIt->second;
- if (touchState.hasTouchingPointers(args.deviceId)) {
+ if (touchState.hasTouchingPointers(args.deviceId) ||
+ touchState.hasHoveringPointers(args.deviceId)) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
@@ -4452,14 +4468,14 @@
// Just enqueue a new motion event.
std::unique_ptr<MotionEntry> newEntry =
- std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source,
- args.displayId, policyFlags, args.action,
- args.actionButton, args.flags, args.metaState,
- args.buttonState, args.classification, args.edgeFlags,
- args.xPrecision, args.yPrecision,
- args.xCursorPosition, args.yCursorPosition,
- args.downTime, args.pointerProperties,
- args.pointerCoords);
+ std::make_unique<MotionEntry>(args.id, /*injectionState=*/nullptr, args.eventTime,
+ args.deviceId, args.source, args.displayId,
+ policyFlags, args.action, args.actionButton,
+ args.flags, args.metaState, args.buttonState,
+ args.classification, args.edgeFlags, args.xPrecision,
+ args.yPrecision, args.xCursorPosition,
+ args.yCursorPosition, args.downTime,
+ args.pointerProperties, args.pointerCoords);
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
@@ -4605,6 +4621,9 @@
resolvedDeviceId = event->getDeviceId();
}
+ const bool isAsync = syncMode == InputEventInjectionSync::NONE;
+ auto injectionState = std::make_shared<InjectionState>(targetUid, isAsync);
+
std::queue<std::unique_ptr<EventEntry>> injectedEntries;
switch (event->getType()) {
case InputEventType::KEY: {
@@ -4637,10 +4656,11 @@
mLock.lock();
std::unique_ptr<KeyEntry> injectedEntry =
- std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
- resolvedDeviceId, incomingKey.getSource(),
- incomingKey.getDisplayId(), policyFlags, action,
- flags, keyCode, incomingKey.getScanCode(), metaState,
+ std::make_unique<KeyEntry>(incomingKey.getId(), injectionState,
+ incomingKey.getEventTime(), resolvedDeviceId,
+ incomingKey.getSource(), incomingKey.getDisplayId(),
+ policyFlags, action, flags, keyCode,
+ incomingKey.getScanCode(), metaState,
incomingKey.getRepeatCount(),
incomingKey.getDownTime());
injectedEntries.push(std::move(injectedEntry));
@@ -4680,9 +4700,10 @@
const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords();
std::unique_ptr<MotionEntry> injectedEntry =
- std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
- resolvedDeviceId, motionEvent.getSource(),
- displayId, policyFlags, motionEvent.getAction(),
+ std::make_unique<MotionEntry>(motionEvent.getId(), injectionState,
+ *sampleEventTimes, resolvedDeviceId,
+ motionEvent.getSource(), displayId, policyFlags,
+ motionEvent.getAction(),
motionEvent.getActionButton(), flags,
motionEvent.getMetaState(),
motionEvent.getButtonState(),
@@ -4702,9 +4723,10 @@
sampleEventTimes += 1;
samplePointerCoords += motionEvent.getPointerCount();
std::unique_ptr<MotionEntry> nextInjectedEntry = std::make_unique<
- MotionEntry>(motionEvent.getId(), *sampleEventTimes, resolvedDeviceId,
- motionEvent.getSource(), displayId, policyFlags,
- motionEvent.getAction(), motionEvent.getActionButton(), flags,
+ MotionEntry>(motionEvent.getId(), injectionState, *sampleEventTimes,
+ resolvedDeviceId, motionEvent.getSource(), displayId,
+ policyFlags, motionEvent.getAction(),
+ motionEvent.getActionButton(), flags,
motionEvent.getMetaState(), motionEvent.getButtonState(),
motionEvent.getClassification(), motionEvent.getEdgeFlags(),
motionEvent.getXPrecision(), motionEvent.getYPrecision(),
@@ -4726,14 +4748,6 @@
return InputEventInjectionResult::FAILED;
}
- InjectionState* injectionState = new InjectionState(targetUid);
- if (syncMode == InputEventInjectionSync::NONE) {
- injectionState->injectionIsAsync = true;
- }
-
- injectionState->refCount += 1;
- injectedEntries.back()->injectionState = injectionState;
-
bool needWake = false;
while (!injectedEntries.empty()) {
if (DEBUG_INJECTION) {
@@ -4796,8 +4810,6 @@
}
}
}
-
- injectionState->release();
} // release lock
if (DEBUG_INJECTION) {
@@ -4843,37 +4855,40 @@
void InputDispatcher::setInjectionResult(EventEntry& entry,
InputEventInjectionResult injectionResult) {
- InjectionState* injectionState = entry.injectionState;
- if (injectionState) {
- if (DEBUG_INJECTION) {
- LOG(INFO) << "Setting input event injection result to "
- << ftl::enum_string(injectionResult);
- }
-
- if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
- // Log the outcome since the injector did not wait for the injection result.
- switch (injectionResult) {
- case InputEventInjectionResult::SUCCEEDED:
- ALOGV("Asynchronous input event injection succeeded.");
- break;
- case InputEventInjectionResult::TARGET_MISMATCH:
- ALOGV("Asynchronous input event injection target mismatch.");
- break;
- case InputEventInjectionResult::FAILED:
- ALOGW("Asynchronous input event injection failed.");
- break;
- case InputEventInjectionResult::TIMED_OUT:
- ALOGW("Asynchronous input event injection timed out.");
- break;
- case InputEventInjectionResult::PENDING:
- ALOGE("Setting result to 'PENDING' for asynchronous injection");
- break;
- }
- }
-
- injectionState->injectionResult = injectionResult;
- mInjectionResultAvailable.notify_all();
+ if (!entry.injectionState) {
+ // Not an injected event.
+ return;
}
+
+ InjectionState& injectionState = *entry.injectionState;
+ if (DEBUG_INJECTION) {
+ LOG(INFO) << "Setting input event injection result to "
+ << ftl::enum_string(injectionResult);
+ }
+
+ if (injectionState.injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
+ // Log the outcome since the injector did not wait for the injection result.
+ switch (injectionResult) {
+ case InputEventInjectionResult::SUCCEEDED:
+ ALOGV("Asynchronous input event injection succeeded.");
+ break;
+ case InputEventInjectionResult::TARGET_MISMATCH:
+ ALOGV("Asynchronous input event injection target mismatch.");
+ break;
+ case InputEventInjectionResult::FAILED:
+ ALOGW("Asynchronous input event injection failed.");
+ break;
+ case InputEventInjectionResult::TIMED_OUT:
+ ALOGW("Asynchronous input event injection timed out.");
+ break;
+ case InputEventInjectionResult::PENDING:
+ ALOGE("Setting result to 'PENDING' for asynchronous injection");
+ break;
+ }
+ }
+
+ injectionState.injectionResult = injectionResult;
+ mInjectionResultAvailable.notify_all();
}
void InputDispatcher::transformMotionEntryForInjectionLocked(
@@ -4900,18 +4915,16 @@
}
void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
- InjectionState* injectionState = entry.injectionState;
- if (injectionState) {
- injectionState->pendingForegroundDispatches += 1;
+ if (entry.injectionState) {
+ entry.injectionState->pendingForegroundDispatches += 1;
}
}
void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) {
- InjectionState* injectionState = entry.injectionState;
- if (injectionState) {
- injectionState->pendingForegroundDispatches -= 1;
+ if (entry.injectionState) {
+ entry.injectionState->pendingForegroundDispatches -= 1;
- if (injectionState->pendingForegroundDispatches == 0) {
+ if (entry.injectionState->pendingForegroundDispatches == 0) {
mInjectionSyncFinished.notify_all();
}
}
@@ -5797,6 +5810,9 @@
dump += INDENT "Connections: <none>\n";
}
+ dump += "input_flags::remove_app_switch_drops() = ";
+ dump += toString(REMOVE_APP_SWITCH_DROPS);
+ dump += "\n";
if (isAppSwitchPendingLocked()) {
dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
ns2ms(mAppSwitchDueTime - now()));
@@ -6128,7 +6144,7 @@
uint32_t seq, bool handled,
nsecs_t consumeTime) {
// Handle post-event policy actions.
- bool restartEvent;
+ std::unique_ptr<KeyEntry> fallbackKeyEntry;
{ // Start critical section
auto dispatchEntryIt =
@@ -6152,15 +6168,9 @@
}
if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) {
- KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry.eventEntry));
- restartEvent =
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(dispatchEntry.eventEntry));
+ fallbackKeyEntry =
afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
- } else if (dispatchEntry.eventEntry->type == EventEntry::Type::MOTION) {
- MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry.eventEntry));
- restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry,
- motionEntry, handled);
- } else {
- restartEvent = false;
}
} // End critical section: The -LockedInterruptable methods may have released the lock.
@@ -6184,12 +6194,13 @@
}
}
traceWaitQueueLength(*connection);
- if (restartEvent && connection->status == Connection::Status::NORMAL) {
- connection->outboundQueue.emplace_front(std::move(dispatchEntry));
- traceOutboundQueueLength(*connection);
- } else {
- releaseDispatchEntry(std::move(dispatchEntry));
+ if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {
+ const InputTarget target{.inputChannel = connection->inputChannel,
+ .flags = dispatchEntry->targetFlags};
+ enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target,
+ InputTarget::Flags::DISPATCH_AS_IS);
}
+ releaseDispatchEntry(std::move(dispatchEntry));
}
// Start the next dispatch cycle for this connection.
@@ -6374,15 +6385,15 @@
sendWindowResponsiveCommandLocked(connectionToken, pid);
}
-bool InputDispatcher::afterKeyEventLockedInterruptable(
+std::unique_ptr<KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable(
const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
- KeyEntry& keyEntry, bool handled) {
+ const KeyEntry& keyEntry, bool handled) {
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
if (!handled) {
// Report the key as unhandled, since the fallback was not handled.
mReporter->reportUnhandledKey(keyEntry.id);
}
- return false;
+ return {};
}
// Get the fallback key state.
@@ -6442,7 +6453,7 @@
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
}
- return false;
+ return {};
}
// Dispatch the unhandled key to the policy.
@@ -6467,7 +6478,7 @@
if (connection->status != Connection::Status::NORMAL) {
connection->inputState.removeFallbackKey(originalKeyCode);
- return false;
+ return {};
}
// Latch the fallback keycode for this key on an initial down.
@@ -6528,25 +6539,22 @@
}
if (fallback) {
- // Restart the dispatch cycle using the fallback key.
- keyEntry.eventTime = event.getEventTime();
- keyEntry.deviceId = event.getDeviceId();
- keyEntry.source = event.getSource();
- keyEntry.displayId = event.getDisplayId();
- keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
- keyEntry.keyCode = *fallbackKeyCode;
- keyEntry.scanCode = event.getScanCode();
- keyEntry.metaState = event.getMetaState();
- keyEntry.repeatCount = event.getRepeatCount();
- keyEntry.downTime = event.getDownTime();
- keyEntry.syntheticRepeat = false;
-
+ // Return the fallback key that we want dispatched to the channel.
+ std::unique_ptr<KeyEntry> newEntry =
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), keyEntry.injectionState,
+ event.getEventTime(), event.getDeviceId(),
+ event.getSource(), event.getDisplayId(),
+ keyEntry.policyFlags, keyEntry.action,
+ event.getFlags() | AKEY_EVENT_FLAG_FALLBACK,
+ *fallbackKeyCode, event.getScanCode(),
+ event.getMetaState(), event.getRepeatCount(),
+ event.getDownTime());
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("Unhandled key event: Dispatching fallback key. "
"originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
originalKeyCode, *fallbackKeyCode, keyEntry.metaState);
}
- return true; // restart the event
+ return newEntry;
} else {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("Unhandled key event: No fallback key.");
@@ -6556,13 +6564,7 @@
mReporter->reportUnhandledKey(keyEntry.id);
}
}
- return false;
-}
-
-bool InputDispatcher::afterMotionEventLockedInterruptable(
- const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
- MotionEntry& motionEntry, bool handled) {
- return false;
+ return {};
}
void InputDispatcher::traceInboundQueueLengthLocked() {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index a1127a0..e9d52cd 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -82,8 +82,6 @@
static constexpr bool kDefaultInTouchMode = true;
explicit InputDispatcher(InputDispatcherPolicyInterface& policy);
- explicit InputDispatcher(InputDispatcherPolicyInterface& policy,
- std::chrono::nanoseconds staleEventTimeout);
~InputDispatcher() override;
void dump(std::string& dump) const override;
@@ -461,9 +459,6 @@
*/
std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
- // Amount of time to allow for an event to be dispatched (measured since its eventTime)
- // before considering it stale and dropping it.
- const std::chrono::nanoseconds mStaleEventTimeout;
bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry);
bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
@@ -664,12 +659,10 @@
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay;
- bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
- DispatchEntry& dispatchEntry, KeyEntry& keyEntry,
- bool handled) REQUIRES(mLock);
- bool afterMotionEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
- DispatchEntry& dispatchEntry, MotionEntry& motionEntry,
- bool handled) REQUIRES(mLock);
+ // Returns a fallback KeyEntry that should be sent to the connection, if required.
+ std::unique_ptr<KeyEntry> afterKeyEventLockedInterruptable(
+ const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
+ const KeyEntry& keyEntry, bool handled) REQUIRES(mLock);
// Find touched state and touched window by token.
std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/>
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 09b5186..17f0b87 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 @@
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 @@
return false;
}
- // We want stylus down to block touch and other source types, but stylus hover should not
- // have such an effect.
- if (isHoverAction(motionEntry.action) && !lastMemento.hovering) {
- // New event is a hover. Keep the current non-hovering gesture instead
- return false;
- }
-
- if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) && !lastMemento.hovering) {
- // We have non-hovering stylus already active.
+ if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties)) {
+ // A stylus is already active.
if (isStylusEvent(motionEntry.source, motionEntry.pointerProperties) &&
actionMasked == AMOTION_EVENT_ACTION_DOWN) {
- // If this new event is a stylus from a different device going down, then cancel the old
- // stylus and allow the new stylus to take over
+ // If this new event is from a different device, then cancel the old
+ // stylus and allow the new stylus to take over, but only if it's going down.
+ // Otherwise, they will start to race each other.
return true;
}
@@ -421,9 +395,10 @@
if (action == AMOTION_EVENT_ACTION_CANCEL) {
flags |= AMOTION_EVENT_FLAG_CANCELED;
}
- return std::make_unique<MotionEntry>(mIdGenerator.nextId(), eventTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags,
- action, /*actionButton=*/0, flags, AMETA_NONE,
+ return std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ eventTime, memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags, action,
+ /*actionButton=*/0, flags, AMETA_NONE,
/*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
@@ -437,9 +412,10 @@
for (KeyMemento& memento : mKeyMementos) {
if (shouldCancelKey(memento, options)) {
events.push_back(
- std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId,
- memento.policyFlags, AKEY_EVENT_ACTION_UP,
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags,
+ AKEY_EVENT_ACTION_UP,
memento.flags | AKEY_EVENT_FLAG_CANCELED,
memento.keyCode, memento.scanCode, memento.metaState,
/*repeatCount=*/0, memento.downTime));
@@ -498,8 +474,8 @@
| (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
- memento.deviceId, memento.source,
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, memento.deviceId, memento.source,
memento.displayId, memento.policyFlags, action,
/*actionButton=*/0, memento.flags, AMETA_NONE,
/*buttonState=*/0, MotionClassification::NONE,
@@ -539,11 +515,11 @@
flags |= AMOTION_EVENT_FLAG_CANCELED;
}
events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId,
- memento.policyFlags, action, /*actionButton=*/0,
- flags, AMETA_NONE, /*buttonState=*/0,
- MotionClassification::NONE,
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags, action,
+ /*actionButton=*/0, flags, AMETA_NONE,
+ /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
memento.yCursorPosition, memento.downTime,
@@ -564,8 +540,8 @@
: AMOTION_EVENT_ACTION_POINTER_UP |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
- memento.deviceId, memento.source,
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+ currentTime, memento.deviceId, memento.source,
memento.displayId, memento.policyFlags, action,
/*actionButton=*/0,
memento.flags | AMOTION_EVENT_FLAG_CANCELED,
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index af28e48..1c23720 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -18,9 +18,12 @@
#include "InputDispatcherConfiguration.h"
+#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>
@@ -118,6 +121,16 @@
/* Poke user activity for an event dispatched to a window. */
virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
+ /*
+ * Return true if the provided event is stale, and false otherwise. Used for determining
+ * whether the dispatcher should drop the event.
+ */
+ virtual bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) {
+ static const std::chrono::duration STALE_EVENT_TIMEOUT =
+ std::chrono::seconds(10) * android::base::HwTimeoutMultiplier();
+ return std::chrono::nanoseconds(currentTime - eventTime) >= STALE_EVENT_TIMEOUT;
+ }
+
/* Notifies the policy that a pointer down event has occurred outside the current focused
* window.
*
@@ -135,7 +148,7 @@
virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
/* Notifies the policy that there was an input device interaction with apps. */
- virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) = 0;
};
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 25e1d21..efc8b26 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -451,6 +451,15 @@
/* Returns true if any InputConnection is currently active. */
virtual bool isInputMethodConnectionActive() = 0;
+
+ /* Gets the viewport of a particular display that the pointer device is associated with. If
+ * the pointer device is not associated with any display, it should ADISPLAY_IS_NONE to get
+ * the viewport that should be used. The device should get a new viewport using this method
+ * every time there is a display configuration change. The logical bounds of the viewport should
+ * be used as the range of possible values for pointing devices, like mice and touchpads.
+ */
+ virtual std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
+ int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 9e020c7..8b47b55 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -38,7 +38,16 @@
* library, libinputservice, that has the additional dependencies. The PointerController
* will be mocked when testing PointerChoreographer.
*/
- virtual std::shared_ptr<PointerControllerInterface> createPointerController() = 0;
+ virtual std::shared_ptr<PointerControllerInterface> createPointerController(
+ PointerControllerInterface::ControllerType type) = 0;
+
+ /**
+ * Notifies the policy that the default pointer displayId has changed. PointerChoreographer is
+ * the single source of truth for all pointers on screen.
+ * @param displayId The updated display on which the mouse cursor is shown
+ * @param position The new position of the mouse cursor on the logical display
+ */
+ virtual void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 95f819a..ef74a55 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -48,10 +48,30 @@
*/
class PointerControllerInterface {
protected:
- PointerControllerInterface() { }
- virtual ~PointerControllerInterface() { }
+ PointerControllerInterface() {}
+ virtual ~PointerControllerInterface() {}
public:
+ /**
+ * Enum used to differentiate various types of PointerControllers for the transition to
+ * using PointerChoreographer.
+ *
+ * TODO(b/293587049): Refactor the PointerController class into different controller types.
+ */
+ enum class ControllerType {
+ // The PointerController that is responsible for drawing all icons.
+ LEGACY,
+ // Represents a single mouse pointer.
+ MOUSE,
+ // Represents multiple touch spots.
+ TOUCH,
+ // Represents a single stylus pointer.
+ STYLUS,
+ };
+
+ /* Dumps the state of the pointer controller. */
+ virtual std::string dump() = 0;
+
/* Gets the bounds of the region that the pointer can traverse.
* Returns true if the bounds are available. */
virtual std::optional<FloatRect> getBounds() const = 0;
@@ -105,7 +125,7 @@
* pressed (not hovering).
*/
virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId) = 0;
+ BitSet32 spotIdBits, int32_t displayId) = 0;
/* Removes all spots. */
virtual void clearSpots() = 0;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 5766b14..0582649 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -1046,6 +1046,14 @@
return mReader->mPreventingTouchpadTaps;
}
+void InputReader::ContextImpl::setLastKeyDownTimestamp(nsecs_t when) {
+ mReader->mLastKeyDownTimestamp = when;
+}
+
+nsecs_t InputReader::ContextImpl::getLastKeyDownTimestamp() {
+ return mReader->mLastKeyDownTimestamp;
+}
+
void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
// lock is already held by the input loop
mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9a297c9..4c78db3 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -158,6 +158,9 @@
void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock)
REQUIRES(mLock) override;
bool isPreventingTouchpadTaps() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
+ void setLastKeyDownTimestamp(nsecs_t when) REQUIRES(mReader->mLock)
+ REQUIRES(mLock) override;
+ nsecs_t getLastKeyDownTimestamp() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
} mContext;
friend class ContextImpl;
@@ -198,6 +201,9 @@
// true if tap-to-click on touchpad currently disabled
bool mPreventingTouchpadTaps GUARDED_BY(mLock){false};
+ // records timestamp of the last key press on the physical keyboard
+ nsecs_t mLastKeyDownTimestamp GUARDED_BY(mLock){0};
+
// low-level input event decoding and device management
[[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index aed7563..69b2315 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -65,6 +65,9 @@
virtual void setPreventingTouchpadTaps(bool prevent) = 0;
virtual bool isPreventingTouchpadTaps() = 0;
+
+ virtual void setLastKeyDownTimestamp(nsecs_t when) = 0;
+ virtual nsecs_t getLastKeyDownTimestamp() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 79f07a5..7aeb215 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -20,6 +20,7 @@
#include "CursorInputMapper.h"
+#include <com_android_input_flags.h>
#include <optional>
#include "CursorButtonAccumulator.h"
@@ -29,6 +30,8 @@
#include "input/PrintTools.h"
+namespace input_flags = com::android::input::flags;
+
namespace android {
// The default velocity control parameters that has no effect.
@@ -71,7 +74,8 @@
CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
: InputMapper(deviceContext, readerConfig),
- mLastEventTime(std::numeric_limits<nsecs_t>::min()) {}
+ mLastEventTime(std::numeric_limits<nsecs_t>::min()),
+ mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {}
CursorInputMapper::~CursorInputMapper() {
if (mPointerController != nullptr) {
@@ -87,11 +91,11 @@
InputMapper::populateDeviceInfo(info);
if (mParameters.mode == Parameters::Mode::POINTER) {
- if (const auto bounds = mPointerController->getBounds(); bounds) {
- info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, bounds->left, bounds->right, 0.0f,
- 0.0f, 0.0f);
- info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, bounds->top, bounds->bottom, 0.0f,
- 0.0f, 0.0f);
+ if (!mBoundsInLogicalDisplay.isEmpty()) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, mBoundsInLogicalDisplay.left,
+ mBoundsInLogicalDisplay.right, 0.0f, 0.0f, 0.0f);
+ info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, mBoundsInLogicalDisplay.top,
+ mBoundsInLogicalDisplay.bottom, 0.0f, 0.0f, 0.0f);
}
} else {
info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
@@ -283,19 +287,22 @@
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mSource == AINPUT_SOURCE_MOUSE) {
- if (moved || scrolled || buttonsChanged) {
- mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ if (!mEnablePointerChoreographer) {
+ if (moved || scrolled || buttonsChanged) {
+ mPointerController->setPresentation(
+ PointerControllerInterface::Presentation::POINTER);
- if (moved) {
- mPointerController->move(deltaX, deltaY);
+ if (moved) {
+ mPointerController->move(deltaX, deltaY);
+ }
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
+
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
}
-
- std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
-
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
} else {
@@ -499,31 +506,55 @@
const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
mDisplayId = ADISPLAY_ID_NONE;
- if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+ std::optional<DisplayViewport> resolvedViewport;
+ bool isBoundsSet = false;
+ if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) {
// This InputDevice is associated with a viewport.
// Only generate events for the associated display.
- const bool mismatchedPointerDisplay =
- isPointer && (viewport->displayId != mPointerController->getDisplayId());
- mDisplayId =
- mismatchedPointerDisplay ? std::nullopt : std::make_optional(viewport->displayId);
+ mDisplayId = assocViewport->displayId;
+ resolvedViewport = *assocViewport;
+ if (!mEnablePointerChoreographer) {
+ const bool mismatchedPointerDisplay =
+ isPointer && (assocViewport->displayId != mPointerController->getDisplayId());
+ if (mismatchedPointerDisplay) {
+ // This device's associated display doesn't match PointerController's current
+ // display. Do not associate it with any display.
+ mDisplayId.reset();
+ }
+ }
} else if (isPointer) {
// The InputDevice is not associated with a viewport, but it controls the mouse pointer.
- mDisplayId = mPointerController->getDisplayId();
+ if (mEnablePointerChoreographer) {
+ // Always use DISPLAY_ID_NONE for mouse events.
+ // PointerChoreographer will make it target the correct the displayId later.
+ const auto pointerViewport =
+ getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
+ mDisplayId = pointerViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
+ resolvedViewport = pointerViewport;
+ } else {
+ mDisplayId = mPointerController->getDisplayId();
+ if (auto v = config.getDisplayViewportById(*mDisplayId); v) {
+ resolvedViewport = *v;
+ }
+ if (auto bounds = mPointerController->getBounds(); bounds) {
+ mBoundsInLogicalDisplay = *bounds;
+ isBoundsSet = true;
+ }
+ }
}
- mOrientation = ui::ROTATION_0;
- const bool isOrientedDevice =
- (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
- // InputReader works in the un-rotated display coordinate space, so we don't need to do
- // anything if the device is already orientation-aware. If the device is not
- // orientation-aware, then we need to apply the inverse rotation of the display so that
- // when the display rotation is applied later as a part of the per-window transform, we
- // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
- // rotations and report values directly from the input device.
- if (!isOrientedDevice && mDisplayId && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
- if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
- mOrientation = getInverseRotation(viewport->orientation);
- }
+ mOrientation = (mParameters.orientationAware && mParameters.hasAssociatedDisplay) ||
+ mParameters.mode == Parameters::Mode::POINTER_RELATIVE || !resolvedViewport
+ ? ui::ROTATION_0
+ : getInverseRotation(resolvedViewport->orientation);
+
+ if (!isBoundsSet) {
+ mBoundsInLogicalDisplay = resolvedViewport
+ ? FloatRect{static_cast<float>(resolvedViewport->logicalLeft),
+ static_cast<float>(resolvedViewport->logicalTop),
+ static_cast<float>(resolvedViewport->logicalRight - 1),
+ static_cast<float>(resolvedViewport->logicalBottom - 1)}
+ : FloatRect{0, 0, 0, 0};
}
bumpGeneration();
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index b879bfd..308adaa 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -119,7 +119,8 @@
// ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
// std::nullopt), all events will be ignored.
std::optional<int32_t> mDisplayId;
- ui::Rotation mOrientation;
+ ui::Rotation mOrientation{ui::ROTATION_0};
+ FloatRect mBoundsInLogicalDisplay{};
std::shared_ptr<PointerControllerInterface> mPointerController;
@@ -127,6 +128,8 @@
nsecs_t mDownTime;
nsecs_t mLastEventTime;
+ const bool mEnablePointerChoreographer;
+
explicit CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
void dumpParameters(std::string& dump);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 531fc67..f068cc8 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -270,7 +270,7 @@
keyDown.flags = flags;
mKeyDowns.push_back(keyDown);
}
- onKeyDownProcessed();
+ onKeyDownProcessed(downTime);
} else {
// Remove key down.
if (keyDownIndex) {
@@ -448,8 +448,9 @@
return out;
}
-void KeyboardInputMapper::onKeyDownProcessed() {
+void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) {
InputReaderContext& context = *getContext();
+ context.setLastKeyDownTimestamp(downTime);
if (context.isPreventingTouchpadTaps()) {
// avoid pinging java service unnecessarily, just fade pointer again if it became visible
context.fadePointer();
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 09808df..500256b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -107,7 +107,7 @@
void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
- void onKeyDownProcessed();
+ void onKeyDownProcessed(nsecs_t downTime);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index f70be72..b0fc903 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -152,6 +152,14 @@
}
}
+size_t MultiTouchMotionAccumulator::getActiveSlotsCount() const {
+ if (!mUsingSlotsProtocol) {
+ return mCurrentSlot < 0 ? 0 : mCurrentSlot;
+ }
+ return std::count_if(mSlots.begin(), mSlots.end(),
+ [](const Slot& slot) { return slot.mInUse; });
+}
+
// --- MultiTouchMotionAccumulator::Slot ---
ToolType MultiTouchMotionAccumulator::Slot::getToolType() const {
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 943dde5..0e3e2bb 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -77,6 +77,7 @@
void process(const RawEvent* rawEvent);
void finishSync();
+ size_t getActiveSlotsCount() const;
inline size_t getSlotCount() const { return mSlots.size(); }
inline const Slot& getSlot(size_t index) const {
LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index);
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 7006e9e..4d2b66d 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -20,6 +20,7 @@
#include <sstream>
#include <android-base/stringprintf.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <linux/input-event-codes.h>
#include <log/log_main.h>
@@ -28,10 +29,14 @@
#include "TouchCursorInputMapperCommon.h"
#include "input/Input.h"
+namespace input_flags = com::android::input::flags;
+
namespace android {
namespace {
+const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection();
+
uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
switch (gesturesButton) {
case GESTURES_BUTTON_LEFT:
@@ -158,7 +163,7 @@
const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
- if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
+ if (ENABLE_TOUCHPAD_PALM_REJECTION && (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
enableTapToClick();
}
rotateDelta(mOrientation, &deltaX, &deltaY);
@@ -200,7 +205,7 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
- if (mReaderContext.isPreventingTouchpadTaps()) {
+ if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) {
enableTapToClick();
if (gesture.details.buttons.is_tap) {
// return early to prevent this tap
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
index be2bfed..69264f8 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -239,7 +239,7 @@
// Helper to std::visit with lambdas.
template <typename... V>
-struct Visitor : V... {};
+struct Visitor : V... { using V::operator()...; };
// explicit deduction guide (not needed as of C++20)
template <typename... V>
Visitor(V...) -> Visitor<V...>;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 64e8825..db31ded 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -58,6 +58,7 @@
"InputReader_test.cpp",
"InstrumentedInputReader.cpp",
"LatencyTracker_test.cpp",
+ "MultiTouchMotionAccumulator_test.cpp",
"NotifyArgs_test.cpp",
"PointerChoreographer_test.cpp",
"PreferStylusOverTouch_test.cpp",
diff --git a/services/inputflinger/tests/FakeApplicationHandle.h b/services/inputflinger/tests/FakeApplicationHandle.h
new file mode 100644
index 0000000..2f634d5
--- /dev/null
+++ b/services/inputflinger/tests/FakeApplicationHandle.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/properties.h>
+#include <android/os/IInputConstants.h>
+#include <gui/InputApplication.h>
+
+namespace android {
+
+namespace inputdispatcher {
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+ FakeApplicationHandle() {
+ static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds(
+ android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ android::base::HwTimeoutMultiplier());
+ mInfo.name = "Fake Application";
+ mInfo.token = sp<BBinder>::make();
+ mInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
+ }
+ virtual ~FakeApplicationHandle() {}
+
+ bool updateInfo() override { return true; }
+
+ void setDispatchingTimeout(std::chrono::milliseconds timeout) {
+ mInfo.dispatchingTimeoutMillis = timeout.count();
+ }
+};
+
+} // namespace inputdispatcher
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
new file mode 100644
index 0000000..e9d93af
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include "InputDispatcherPolicyInterface.h"
+
+namespace android {
+
+// --- FakeInputDispatcherPolicy ---
+
+class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
+public:
+ FakeInputDispatcherPolicy() = default;
+ virtual ~FakeInputDispatcherPolicy() = default;
+
+private:
+ void notifyConfigurationChanged(nsecs_t) override {}
+
+ void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
+ LOG(ERROR) << "There is no focused window for " << applicationHandle->getName();
+ }
+
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
+ const std::string& reason) override {
+ LOG(ERROR) << "Window is not responding: " << reason;
+ }
+
+ void notifyWindowResponsive(const sp<IBinder>& connectionToken,
+ std::optional<gui::Pid> pid) override {}
+
+ void notifyInputChannelBroken(const sp<IBinder>&) override {}
+
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+
+ void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+ const std::vector<float>& values) override {}
+
+ void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) override {}
+
+ void notifyVibratorState(int32_t deviceId, bool isOn) override {}
+
+ bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
+ return true; // dispatch event normally
+ }
+
+ void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}
+
+ void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
+ return 0;
+ }
+
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+ uint32_t) override {
+ return {};
+ }
+
+ void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
+
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+
+ void setPointerCapture(const PointerCaptureRequest&) override {}
+
+ void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
+
+ void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) override {}
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 41c98ef..88f514f 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -261,4 +261,17 @@
mStylusGestureNotified = deviceId;
}
+std::optional<DisplayViewport> FakeInputReaderPolicy::getPointerViewportForAssociatedDisplay(
+ int32_t associatedDisplayId) {
+ if (associatedDisplayId == ADISPLAY_ID_NONE) {
+ associatedDisplayId = mConfig.defaultPointerDisplayId;
+ }
+ for (auto& viewport : mViewports) {
+ if (viewport.displayId == associatedDisplayId) {
+ return std::make_optional(viewport);
+ }
+ }
+ return std::nullopt;
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 48912a6..4ef9c3e 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -79,6 +79,8 @@
void setStylusPointerIconEnabled(bool enabled);
void setIsInputMethodConnectionActive(bool active);
bool isInputMethodConnectionActive() override;
+ std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
+ int32_t associatedDisplayId) override;
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index ca517f3..5475594 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -47,6 +47,8 @@
void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) {
mDisplayId = viewport.displayId;
+ setBounds(viewport.logicalLeft, viewport.logicalTop, viewport.logicalRight - 1,
+ viewport.logicalBottom - 1);
}
void FakePointerController::assertPosition(float x, float y) {
@@ -55,6 +57,12 @@
ASSERT_NEAR(y, actualY, 1);
}
+void FakePointerController::assertSpotCount(int32_t displayId, int32_t count) {
+ auto it = mSpotsByDisplay.find(displayId);
+ ASSERT_TRUE(it != mSpotsByDisplay.end()) << "Spots not found for display " << displayId;
+ ASSERT_EQ(static_cast<size_t>(count), it->second.size());
+}
+
bool FakePointerController::isPointerShown() {
return mIsPointerShown;
}
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index c374267..d7e40b3 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -37,9 +37,11 @@
void setDisplayViewport(const DisplayViewport& viewport) override;
void assertPosition(float x, float y);
+ void assertSpotCount(int32_t displayId, int32_t count);
bool isPointerShown();
private:
+ std::string dump() override { return ""; }
std::optional<FloatRect> getBounds() const override;
void move(float deltaX, float deltaY) override;
void fade(Transition) override;
@@ -52,7 +54,7 @@
bool mHaveBounds{false};
float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
float mX{0}, mY{0};
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ int32_t mDisplayId{ADISPLAY_ID_NONE};
bool mIsPointerShown{false};
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h
new file mode 100644
index 0000000..fe25130
--- /dev/null
+++ b/services/inputflinger/tests/FakeWindowHandle.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include "../dispatcher/InputDispatcher.h"
+
+using android::base::Result;
+using android::gui::Pid;
+using android::gui::TouchOcclusionMode;
+using android::gui::Uid;
+using android::gui::WindowInfo;
+using android::gui::WindowInfoHandle;
+
+namespace android {
+namespace inputdispatcher {
+
+namespace {
+
+// The default pid and uid for windows created by the test.
+constexpr gui::Pid WINDOW_PID{999};
+constexpr gui::Uid WINDOW_UID{1001};
+
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
+
+} // namespace
+
+class FakeInputReceiver {
+public:
+ std::unique_ptr<InputEvent> consumeEvent(std::chrono::milliseconds timeout) {
+ uint32_t consumeSeq = 0;
+ std::unique_ptr<InputEvent> event;
+
+ std::chrono::time_point start = std::chrono::steady_clock::now();
+ status_t result = WOULD_BLOCK;
+ while (result == WOULD_BLOCK) {
+ InputEvent* rawEventPtr = nullptr;
+ result = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
+ &rawEventPtr);
+ event = std::unique_ptr<InputEvent>(rawEventPtr);
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+ if (elapsed > timeout) {
+ if (timeout != 0ms) {
+ LOG(ERROR) << "Waited too long for consumer to produce an event, giving up";
+ }
+ break;
+ }
+ }
+ // Events produced by this factory are owned pointers.
+ if (result != OK) {
+ if (timeout == 0ms) {
+ // This is likely expected. No need to log.
+ } else {
+ LOG(ERROR) << "Received result = " << result << " from consume";
+ }
+ return nullptr;
+ }
+ result = mConsumer.sendFinishedSignal(consumeSeq, true);
+ if (result != OK) {
+ LOG(ERROR) << "Received result = " << result << " from sendFinishedSignal";
+ }
+ return event;
+ }
+
+ explicit FakeInputReceiver(std::unique_ptr<InputChannel> channel, const std::string name)
+ : mConsumer(std::move(channel)) {}
+
+ virtual ~FakeInputReceiver() {}
+
+private:
+ std::unique_ptr<InputChannel> mClientChannel;
+ InputConsumer mConsumer;
+ DynamicInputEventFactory mEventFactory;
+};
+
+class FakeWindowHandle : public WindowInfoHandle {
+public:
+ static const int32_t WIDTH = 600;
+ static const int32_t HEIGHT = 800;
+
+ FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ InputDispatcher& dispatcher, const std::string name, int32_t displayId)
+ : mName(name) {
+ Result<std::unique_ptr<InputChannel>> channel = dispatcher.createInputChannel(name);
+ mInfo.token = (*channel)->getConnectionToken();
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+
+ mInfo.id = sId++;
+ mInfo.name = name;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.alpha = 1.0;
+ mInfo.frame.left = 0;
+ mInfo.frame.top = 0;
+ mInfo.frame.right = WIDTH;
+ mInfo.frame.bottom = HEIGHT;
+ mInfo.transform.set(0, 0);
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.ownerPid = WINDOW_PID;
+ mInfo.ownerUid = WINDOW_UID;
+ mInfo.displayId = displayId;
+ mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
+ }
+
+ sp<FakeWindowHandle> clone(int32_t displayId) {
+ sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)");
+ handle->mInfo = mInfo;
+ handle->mInfo.displayId = displayId;
+ handle->mInfo.id = sId++;
+ handle->mInputReceiver = mInputReceiver;
+ return handle;
+ }
+
+ void setTouchable(bool touchable) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable);
+ }
+
+ void setFocusable(bool focusable) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable);
+ }
+
+ void setVisible(bool visible) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+ }
+
+ void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+ mInfo.dispatchingTimeout = timeout;
+ }
+
+ void setPaused(bool paused) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::PAUSE_DISPATCHING, paused);
+ }
+
+ void setPreventSplitting(bool preventSplitting) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::PREVENT_SPLITTING, preventSplitting);
+ }
+
+ void setSlippery(bool slippery) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::SLIPPERY, slippery);
+ }
+
+ void setWatchOutsideTouch(bool watchOutside) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
+ }
+
+ void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); }
+
+ void setInterceptsStylus(bool interceptsStylus) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus);
+ }
+
+ void setDropInput(bool dropInput) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput);
+ }
+
+ void setDropInputIfObscured(bool dropInputIfObscured) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured);
+ }
+
+ void setNoInputChannel(bool noInputChannel) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
+ }
+
+ void setDisableUserActivity(bool disableUserActivity) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
+ }
+
+ void setAlpha(float alpha) { mInfo.alpha = alpha; }
+
+ void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
+
+ void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
+
+ void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) {
+ mInfo.frame.left = frame.left;
+ mInfo.frame.top = frame.top;
+ mInfo.frame.right = frame.right;
+ mInfo.frame.bottom = frame.bottom;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(frame);
+
+ const Rect logicalDisplayFrame = displayTransform.transform(frame);
+ ui::Transform translate;
+ translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top);
+ mInfo.transform = translate * displayTransform;
+ }
+
+ void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
+
+ void setIsWallpaper(bool isWallpaper) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::IS_WALLPAPER, isWallpaper);
+ }
+
+ void setDupTouchToWallpaper(bool hasWallpaper) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
+ }
+
+ void setTrustedOverlay(bool trustedOverlay) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay);
+ }
+
+ void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
+ mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
+ }
+
+ void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
+
+ void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
+
+ std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout) {
+ if (mInputReceiver == nullptr) {
+ return nullptr;
+ }
+ return mInputReceiver->consumeEvent(timeout);
+ }
+
+ void consumeMotion() {
+ std::unique_ptr<InputEvent> event = consume(100ms);
+
+ if (event == nullptr) {
+ LOG(FATAL) << mName << ": expected a MotionEvent, but didn't get one.";
+ return;
+ }
+
+ if (event->getType() != InputEventType::MOTION) {
+ LOG(FATAL) << mName << " expected a MotionEvent, got " << *event;
+ return;
+ }
+ }
+
+ sp<IBinder> getToken() { return mInfo.token; }
+
+ const std::string& getName() { return mName; }
+
+ void setOwnerInfo(Pid ownerPid, Uid ownerUid) {
+ mInfo.ownerPid = ownerPid;
+ mInfo.ownerUid = ownerUid;
+ }
+
+ Pid getPid() const { return mInfo.ownerPid; }
+
+ void destroyReceiver() { mInputReceiver = nullptr; }
+
+private:
+ FakeWindowHandle(std::string name) : mName(name){};
+ const std::string mName;
+ std::shared_ptr<FakeInputReceiver> mInputReceiver;
+ static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
+ friend class sp<FakeWindowHandle>;
+};
+
+std::atomic<int32_t> FakeWindowHandle::sId{1};
+
+} // namespace inputdispatcher
+
+} // namespace android
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index d7dc800..41c7392 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -16,7 +16,8 @@
#include <memory>
-#include <EventHub.h>
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gestures/GestureConverter.h>
#include <gtest/gtest.h>
#include <gui/constants.h>
@@ -34,6 +35,13 @@
namespace android {
+namespace {
+
+const auto TOUCHPAD_PALM_REJECTION =
+ ACONFIG_FLAG(com::android::input::flags, enable_touchpad_typing_palm_rejection);
+
+} // namespace
+
using testing::AllOf;
class GestureConverterTest : public testing::Test {
@@ -1161,7 +1169,8 @@
WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
-TEST_F(GestureConverterTest, TapWithTapToClickDisabled) {
+TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
// Tap should be ignored when disabled
mReader->getContext()->setPreventingTouchpadTaps(true);
@@ -1193,7 +1202,8 @@
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
-TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
+TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
// Click should still produce button press/release events
mReader->getContext()->setPreventingTouchpadTaps(true);
@@ -1260,7 +1270,8 @@
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
-TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
+TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
// initially disable tap-to-click
mReader->getContext()->setPreventingTouchpadTaps(true);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3c87f71..4d7a170 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>
@@ -112,8 +113,6 @@
// An arbitrary pid of the gesture monitor window
static constexpr gui::Pid MONITOR_PID{2001};
-static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
-
/**
* If we expect to receive the event, the timeout can be made very long. When the test are running
* correctly, we will actually never wait until the end of the timeout because the wait will end
@@ -337,7 +336,7 @@
std::optional<sp<IBinder>> receivedToken =
getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock,
mNotifyInputChannelBroken);
- ASSERT_TRUE(receivedToken.has_value());
+ ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token";
ASSERT_EQ(token, *receivedToken);
}
@@ -348,6 +347,8 @@
mInterceptKeyTimeout = timeout;
}
+ void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; }
+
void assertUserActivityPoked() {
std::scoped_lock lock(mLock);
ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked";
@@ -366,6 +367,30 @@
ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms));
}
+ void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler) {
+ std::scoped_lock lock(mLock);
+ mUnhandledKeyHandler = handler;
+ }
+
+ void assertUnhandledKeyReported(int32_t keycode) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<int32_t> unhandledKeycode =
+ getItemFromStorageLockedInterruptible(100ms, mReportedUnhandledKeycodes, lock,
+ mNotifyUnhandledKey);
+ ASSERT_TRUE(unhandledKeycode) << "Expected unhandled key to be reported";
+ ASSERT_EQ(unhandledKeycode, keycode);
+ }
+
+ void assertUnhandledKeyNotReported() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<int32_t> unhandledKeycode =
+ getItemFromStorageLockedInterruptible(10ms, mReportedUnhandledKeycodes, lock,
+ mNotifyUnhandledKey);
+ ASSERT_FALSE(unhandledKeycode) << "Expected unhandled key NOT to be reported";
+ }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -391,8 +416,14 @@
std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
+ std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
+
BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
+ std::condition_variable mNotifyUnhandledKey;
+ std::queue<int32_t> mReportedUnhandledKeycodes GUARDED_BY(mLock);
+ std::function<std::optional<KeyEvent>(const KeyEvent&)> mUnhandledKeyHandler GUARDED_BY(mLock);
+
// All three ANR-related callbacks behave the same way, so we use this generic function to wait
// for a specific container to become non-empty. When the container is non-empty, return the
// first entry from the container and erase it.
@@ -435,7 +466,6 @@
condition.wait_for(lock, timeout,
[&storage]() REQUIRES(mLock) { return !storage.empty(); });
if (storage.empty()) {
- ADD_FAILURE() << "Did not receive the expected callback";
return std::nullopt;
}
T item = storage.front();
@@ -526,9 +556,12 @@
return delay;
}
- std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event,
uint32_t) override {
- return {};
+ std::scoped_lock lock(mLock);
+ mReportedUnhandledKeycodes.emplace(event.getKeyCode());
+ mNotifyUnhandledKey.notify_all();
+ return mUnhandledKeyHandler != nullptr ? mUnhandledKeyHandler(event) : std::nullopt;
}
void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
@@ -545,6 +578,10 @@
mPokedUserActivity = true;
}
+ bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override {
+ return std::chrono::nanoseconds(currentTime - eventTime) >= mStaleEventTimeout;
+ }
+
void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
std::scoped_lock lock(mLock);
mOnPointerDownToken = newToken;
@@ -586,7 +623,8 @@
void SetUp() override {
mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
- mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, STALE_EVENT_TIMEOUT);
+ mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
+
mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
// Start InputDispatcher thread
ASSERT_EQ(OK, mDispatcher->start());
@@ -814,37 +852,18 @@
android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
android::base::HwTimeoutMultiplier());
-class FakeApplicationHandle : public InputApplicationHandle {
-public:
- FakeApplicationHandle() {
- mInfo.name = "Fake Application";
- mInfo.token = sp<BBinder>::make();
- mInfo.dispatchingTimeoutMillis =
- std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
- }
- virtual ~FakeApplicationHandle() {}
-
- virtual bool updateInfo() override { return true; }
-
- void setDispatchingTimeout(std::chrono::milliseconds timeout) {
- mInfo.dispatchingTimeoutMillis = timeout.count();
- }
-};
-
class FakeInputReceiver {
public:
explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
- : mName(name) {
- mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
- }
+ : mConsumer(std::move(clientChannel)), mName(name) {}
- InputEvent* consume(std::chrono::milliseconds timeout) {
+ InputEvent* consume(std::chrono::milliseconds timeout, bool handled = false) {
InputEvent* event;
std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event);
if (!consumeSeq) {
return nullptr;
}
- finishEvent(*consumeSeq);
+ finishEvent(*consumeSeq, handled);
return event;
}
@@ -860,8 +879,8 @@
std::chrono::time_point start = std::chrono::steady_clock::now();
status_t status = WOULD_BLOCK;
while (status == WOULD_BLOCK) {
- status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
- &event);
+ status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
+ &event);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > timeout) {
break;
@@ -890,13 +909,13 @@
/**
* To be used together with "receiveEvent" to complete the consumption of an event.
*/
- void finishEvent(uint32_t consumeSeq) {
- const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true);
+ void finishEvent(uint32_t consumeSeq, bool handled = true) {
+ const status_t status = mConsumer.sendFinishedSignal(consumeSeq, handled);
ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
- const status_t status = mConsumer->sendTimeline(inputEventId, timeline);
+ const status_t status = mConsumer.sendTimeline(inputEventId, timeline);
ASSERT_EQ(OK, status);
}
@@ -1054,12 +1073,12 @@
<< ": should not have received any events, so consume() should return NULL";
}
- sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
+ sp<IBinder> getToken() { return mConsumer.getChannel()->getConnectionToken(); }
- int getChannelFd() { return mConsumer->getChannel()->getFd().get(); }
+ int getChannelFd() { return mConsumer.getChannel()->getFd().get(); }
-protected:
- std::unique_ptr<InputConsumer> mConsumer;
+private:
+ InputConsumer mConsumer;
PreallocatedInputEventFactory mEventFactory;
std::string mName;
@@ -1202,8 +1221,8 @@
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
- KeyEvent* consumeKey() {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ KeyEvent* consumeKey(bool handled = true) {
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
if (event == nullptr) {
ADD_FAILURE() << "Consume failed : no event";
return nullptr;
@@ -1342,11 +1361,11 @@
mInputReceiver->sendTimeline(inputEventId, timeline);
}
- InputEvent* consume(std::chrono::milliseconds timeout) {
+ InputEvent* consume(std::chrono::milliseconds timeout, bool handled = true) {
if (mInputReceiver == nullptr) {
return nullptr;
}
- return mInputReceiver->consume(timeout);
+ return mInputReceiver->consume(timeout, handled);
}
MotionEvent* consumeMotion() {
@@ -1399,42 +1418,39 @@
class FakeMonitorReceiver {
public:
- FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId) {
- base::Result<std::unique_ptr<InputChannel>> channel =
- dispatcher.createInputMonitor(displayId, name, MONITOR_PID);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
- }
+ FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId)
+ : mInputReceiver(*dispatcher.createInputMonitor(displayId, name, MONITOR_PID), name) {}
- sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+ sp<IBinder> getToken() { return mInputReceiver.getToken(); }
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
- expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+ expectedFlags);
}
std::optional<int32_t> receiveEvent() {
- return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ return mInputReceiver.receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
}
- void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+ void finishEvent(uint32_t consumeSeq) { return mInputReceiver.finishEvent(consumeSeq); }
void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeMotionEvent(
+ mInputReceiver.consumeMotionEvent(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
WithDisplayId(expectedDisplayId),
WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
@@ -1443,20 +1459,20 @@
void consumeMotionPointerDown(int32_t pointerIdx) {
int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
- /*expectedFlags=*/0);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
+ /*expectedFlags=*/0);
}
void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
- mInputReceiver->consumeMotionEvent(matcher);
+ mInputReceiver.consumeMotionEvent(matcher);
}
- MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
+ MotionEvent* consumeMotion() { return mInputReceiver.consumeMotion(); }
- void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+ void assertNoEvents() { mInputReceiver.assertNoEvents(); }
private:
- std::unique_ptr<FakeInputReceiver> mInputReceiver;
+ FakeInputReceiver mInputReceiver;
};
static InputEventInjectionResult injectKey(
@@ -2160,6 +2176,69 @@
}
/**
+ * Same as the above 'TwoPointerCancelInconsistentPolicy' test, but for hovers.
+ * The policy typically sets POLICY_FLAG_PASS_TO_USER to the events. But when the display is not
+ * interactive, it might stop sending this flag.
+ * We've already ensured the consistency of the touch event in this case, and we should also ensure
+ * the consistency of the hover event in this case.
+ *
+ * Test procedure:
+ * HOVER_ENTER -> HOVER_MOVE -> (stop sending POLICY_FLAG_PASS_TO_USER) -> HOVER_EXIT
+ * HOVER_ENTER -> HOVER_MOVE -> HOVER_EXIT
+ *
+ * We expect to receive two full streams of hover events.
+ */
+TEST_F(InputDispatcherTest, HoverEventInconsistentPolicy) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 300, 300));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .policyFlags(DEFAULT_POLICY_FLAGS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(101))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .policyFlags(DEFAULT_POLICY_FLAGS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+
+ // Send hover exit without the default policy flags.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .policyFlags(0)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102))
+ .build());
+
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // Send a simple hover event stream, ensure dispatcher not crashed and window can receive
+ // right event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .policyFlags(DEFAULT_POLICY_FLAGS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(200).y(201))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .policyFlags(DEFAULT_POLICY_FLAGS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(201).y(202))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .policyFlags(DEFAULT_POLICY_FLAGS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(201).y(202))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+}
+
+/**
* Two windows: a window on the left and a window on the right.
* Mouse is hovered from the right window into the left window.
* Next, we tap on the left window, where the cursor was last seen.
@@ -2514,9 +2593,9 @@
/**
* 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);
@@ -2545,34 +2624,29 @@
.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);
@@ -2604,15 +2678,21 @@
.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();
}
/**
@@ -2929,11 +3009,11 @@
* 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 =
@@ -2973,28 +3053,25 @@
.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)));
@@ -3209,7 +3286,7 @@
* While the touch is down, new hover events from the stylus device should be ignored. After the
* touch is gone, stylus hovering should start working again.
*/
-TEST_F(InputDispatcherMultiDeviceTest, 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);
@@ -3235,10 +3312,7 @@
.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,
@@ -3248,7 +3322,9 @@
.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,
@@ -3258,7 +3334,6 @@
.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,
@@ -3267,8 +3342,8 @@
.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();
}
@@ -6464,6 +6539,213 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
}
+class InputDispatcherFallbackKeyTest : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApp = std::make_shared<FakeApplicationHandle>();
+
+ mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
+ setFocusedWindow(mWindow);
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeFocusEvent(/*hasFocus=*/true));
+ }
+
+ void setFallback(int32_t keycode) {
+ mFakePolicy->setUnhandledKeyHandler([keycode](const KeyEvent& event) {
+ return KeyEventBuilder(event).keyCode(keycode).build();
+ });
+ }
+
+ void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) {
+ KeyEvent* event = mWindow->consumeKey(handled);
+ ASSERT_NE(event, nullptr) << "Did not receive key event";
+ ASSERT_THAT(*event, matcher);
+ }
+};
+
+TEST_F(InputDispatcherFallbackKeyTest, PolicyNotNotifiedForHandledKey) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/true, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, PolicyNotifiedForUnhandledKey) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, NoFallbackRequestedByPolicy) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+
+ // Since the policy did not request any fallback to be generated, ensure there are no events.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, FallbackDispatchForUnhandledKey) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+
+ // Since the key was not handled, ensure the fallback event was dispatched instead.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, AppHandlesPreviouslyUnhandledKey) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, but handle the fallback.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ // But this time, the app handles the original key.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key is canceled.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, AppDoesNotHandleFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // App does not handle the fallback either, so ensure another fallback is not generated.
+ setFallback(AKEYCODE_C);
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, InconsistentPolicyCancelsFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, so fallback is generated.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, but assume the policy is misbehaving and it
+ // generates an inconsistent fallback to the one from the DOWN event.
+ setFallback(AKEYCODE_C);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key reported before as DOWN is canceled due to the inconsistency.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, so fallback is generated.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // The original key is canceled.
+ mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD)
+ .keyCode(AKEYCODE_A)
+ .addFlag(AKEY_EVENT_FLAG_CANCELED)
+ .build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A),
+ WithFlags(AKEY_EVENT_FLAG_CANCELED)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key is also canceled due to the original key being canceled.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
@@ -7529,6 +7811,8 @@
mWindow->consumeFocusEvent(false);
KeyEvent event;
+ static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+ mFakePolicy->setStaleEventTimeout(STALE_EVENT_TIMEOUT);
const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC) -
std::chrono::nanoseconds(STALE_EVENT_TIMEOUT).count();
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index dac4ea0..787444c 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -80,9 +80,15 @@
}
std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, int32_t value) {
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ return process(when, type, code, value);
+}
+
+std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, int32_t type, int32_t code,
+ int32_t value) {
RawEvent event;
- event.when = systemTime(SYSTEM_TIME_MONOTONIC);
- event.readTime = event.when;
+ event.when = when;
+ event.readTime = when;
event.deviceId = mMapper->getDeviceContext().getEventHubId();
event.type = type;
event.code = code;
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index c2ac258..3f9061f 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -52,6 +52,7 @@
void setKeyCodeState(KeyState state, std::set<int> keyCodes);
std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value);
+ std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value);
MockEventHubInterface mMockEventHub;
std::shared_ptr<FakePointerController> mFakePointerController;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 64ae9e8..5044b3e 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -37,6 +37,7 @@
#include <UinputDevice.h>
#include <VibratorInputMapper.h>
#include <android-base/thread_annotations.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <gtest/gtest.h>
#include <gui/constants.h>
@@ -99,6 +100,8 @@
// Maximum smoothing time delta so that we don't generate events too far into the future.
constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
+namespace input_flags = com::android::input::flags;
+
template<typename T>
static inline T min(T a, T b) {
return a < b ? a : b;
@@ -4097,9 +4100,9 @@
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
-// --- CursorInputMapperTest ---
+// --- CursorInputMapperTestBase ---
-class CursorInputMapperTest : public InputMapperTest {
+class CursorInputMapperTestBase : public InputMapperTest {
protected:
static const int32_t TRACKBALL_MOVEMENT_THRESHOLD;
@@ -4133,11 +4136,11 @@
}
};
-const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
+const int32_t CursorInputMapperTestBase::TRACKBALL_MOVEMENT_THRESHOLD = 6;
-void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_t originalX,
- int32_t originalY, int32_t rotatedX,
- int32_t rotatedY) {
+void CursorInputMapperTestBase::testMotionRotation(CursorInputMapper& mapper, int32_t originalX,
+ int32_t originalY, int32_t rotatedX,
+ int32_t rotatedY) {
NotifyMotionArgs args;
process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, originalX);
@@ -4151,6 +4154,16 @@
float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f));
}
+// --- CursorInputMapperTest ---
+
+class CursorInputMapperTest : public CursorInputMapperTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ CursorInputMapperTestBase::SetUp();
+ }
+};
+
TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
addConfigurationProperty("cursor.mode", "pointer");
CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
@@ -4180,6 +4193,7 @@
// When the bounds are set, then there should be a valid motion range.
mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
InputDeviceInfo info2;
mapper.populateDeviceInfo(info2);
@@ -4906,11 +4920,459 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
-// --- BluetoothCursorInputMapperTest ---
+// --- CursorInputMapperTestWithChoreographer ---
-class BluetoothCursorInputMapperTest : public CursorInputMapperTest {
+class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase {
protected:
void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ CursorInputMapperTestBase::SetUp();
+ }
+};
+
+TEST_F(CursorInputMapperTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ InputDeviceInfo info;
+ mapper.populateDeviceInfo(info);
+
+ // Initially there may not be a valid motion range.
+ ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
+ ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+ AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
+
+ // When the viewport and the default pointer display ID is set, then there should be a valid
+ // motion range.
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ InputDeviceInfo info2;
+ mapper.populateDeviceInfo(info2);
+
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0,
+ DISPLAY_WIDTH - 1, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0,
+ DISPLAY_HEIGHT - 1, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
+ AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ prepareDisplay(ui::ROTATION_0);
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ NotifyMotionArgs motionArgs;
+ NotifyKeyArgs keyArgs;
+
+ // press BTN_LEFT, release BTN_LEFT
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+ motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ // press BTN_BACK, release BTN_BACK
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+ ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+ ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+ // press BTN_SIDE, release BTN_SIDE
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+ ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+ ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+ // press BTN_FORWARD, release BTN_FORWARD
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+ ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+ ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+ // press BTN_EXTRA, release BTN_EXTRA
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+ ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.buttonState);
+ ASSERT_NO_FATAL_FAILURE(
+ assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+ ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ prepareDisplay(ui::ROTATION_0);
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ NotifyMotionArgs args;
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
+ /*highThreshold=*/100.f, /*acceleration=*/10.f);
+ mFakePolicy->setVelocityControlParams(testParams);
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ prepareDisplay(ui::ROTATION_0);
+
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
+
+ NotifyMotionArgs args;
+
+ // Move and verify scale is applied.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+ const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ ASSERT_GT(relX, 10);
+ ASSERT_GT(relY, 20);
+
+ // Enable Pointer Capture
+ mFakePolicy->setPointerCapture(true);
+ configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
+ NotifyPointerCaptureChangedArgs captureArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
+ ASSERT_TRUE(captureArgs.request.enable);
+
+ // Move and verify scale is not applied.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ const float relX2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float relY2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ ASSERT_EQ(10, relX2);
+ ASSERT_EQ(20, relY2);
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) {
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ // Set up the default display.
+ prepareDisplay(ui::ROTATION_90);
+
+ // Set up the secondary display as the display on which the pointer should be shown.
+ // The InputDevice is not associated with any display.
+ prepareSecondaryDisplay();
+ mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ // Ensure input events are generated without display ID and coords,
+ // because they will be decided later by PointerChoreographer.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE),
+ WithCoords(0.0f, 0.0f))));
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) {
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ // Set up the default display.
+ prepareDisplay(ui::ROTATION_90);
+
+ // Set up the secondary display as the display on which the pointer should be shown,
+ // and associate the InputDevice with the secondary display.
+ prepareSecondaryDisplay();
+ mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+ mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ // Ensure input events are generated with associated display ID but not with coords,
+ // because the coords will be decided later by PointerChoreographer.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+ WithCoords(0.0f, 0.0f))));
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer,
+ ConfigureDisplayIdShouldGenerateEventWithMismatchedPointerDisplay) {
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ // Set up the default display as the display on which the pointer should be shown.
+ prepareDisplay(ui::ROTATION_90);
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+
+ // Associate the InputDevice with the secondary display.
+ prepareSecondaryDisplay();
+ mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // With PointerChoreographer enabled, there could be a PointerController for the associated
+ // display even if it is different from the pointer display. So the mapper should generate an
+ // event.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+ WithCoords(0.0f, 0.0f))));
+}
+
+// --- BluetoothCursorInputMapperTest ---
+
+class BluetoothCursorInputMapperTest : public CursorInputMapperTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
mFakePointerController = std::make_shared<FakePointerController>();
@@ -5006,6 +5468,122 @@
WithEventTime(expectedEventTime))));
}
+// --- BluetoothCursorInputMapperTestWithChoreographer ---
+
+class BluetoothCursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
+
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+ }
+};
+
+TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmoothening) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ // Set up the default display.
+ prepareDisplay(ui::ROTATION_0);
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+
+ // Process several events that come in quick succession, according to their timestamps.
+ for (int i = 0; i < 3; i++) {
+ constexpr static nsecs_t delta = ms2ns(1);
+ static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA);
+ kernelEventTime += delta;
+ expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+ process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningIsCapped) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ // Set up the default display.
+ prepareDisplay(ui::ROTATION_0);
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+
+ // Process several events with the same timestamp from the kernel.
+ // Ensure that we do not generate events too far into the future.
+ constexpr static int32_t numEvents =
+ MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA;
+ for (int i = 0; i < numEvents; i++) {
+ expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+ }
+
+ // By processing more events with the same timestamp, we should not generate events with a
+ // timestamp that is more than the specified max time delta from the timestamp at its injection.
+ const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA;
+ for (int i = 0; i < 3; i++) {
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(cappedEventTime))));
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningNotUsed) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+ // Set up the default display.
+ prepareDisplay(ui::ROTATION_0);
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+
+ // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
+ // smoothening is not needed, its timestamp is not affected.
+ kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1);
+ expectedEventTime = kernelEventTime;
+
+ process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+}
+
// --- TouchInputMapperTest ---
class TouchInputMapperTest : public InputMapperTest {
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index ca85558..e9c7bb4 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -106,6 +106,9 @@
void setPreventingTouchpadTaps(bool prevent) override { mPreventingTouchpadTaps = prevent; }
bool isPreventingTouchpadTaps() override { return mPreventingTouchpadTaps; }
+ void setLastKeyDownTimestamp(nsecs_t when) override { mLastKeyDownTimestamp = when; };
+ nsecs_t getLastKeyDownTimestamp() override { return mLastKeyDownTimestamp; };
+
private:
int32_t mGlobalMetaState;
bool mUpdateGlobalMetaStateWasCalled;
@@ -113,6 +116,7 @@
std::optional<nsecs_t> mRequestedTimeout;
std::vector<InputDeviceInfo> mExternalStylusDevices;
bool mPreventingTouchpadTaps{false};
+ nsecs_t mLastKeyDownTimestamp;
} mFakeContext;
friend class InputReaderTest;
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 05823cd..7394913 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -77,6 +77,9 @@
MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override));
MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override));
+ MOCK_METHOD(void, setLastKeyDownTimestamp, (nsecs_t when));
+ MOCK_METHOD(nsecs_t, getLastKeyDownTimestamp, ());
+
private:
int32_t mGeneration = 0;
};
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 48f5673..2ef7999 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -26,6 +26,7 @@
namespace android {
using testing::_;
+using testing::Args;
using testing::DoAll;
using testing::Return;
using testing::SetArgPointee;
@@ -158,4 +159,18 @@
testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false);
}
+TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) {
+ nsecs_t when = ARBITRARY_TIME;
+ std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT};
+ EXPECT_CALL(mMockInputReaderContext, setLastKeyDownTimestamp)
+ .With(Args<0>(when))
+ .Times(keyCodes.size());
+ for (int32_t keyCode : keyCodes) {
+ process(when, EV_KEY, keyCode, 1);
+ process(when, EV_SYN, SYN_REPORT, 0);
+ process(when, EV_KEY, keyCode, 0);
+ process(when, EV_SYN, SYN_REPORT, 0);
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
new file mode 100644
index 0000000..9fa6cdd
--- /dev/null
+++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MultiTouchMotionAccumulator.h"
+#include "InputMapperTest.h"
+
+namespace android {
+
+class MultiTouchMotionAccumulatorTest : public InputMapperUnitTest {
+protected:
+ static constexpr size_t SLOT_COUNT = 8;
+
+ MultiTouchMotionAccumulator mMotionAccumulator;
+
+ void processMotionEvent(int32_t type, int32_t code, int32_t value) {
+ RawEvent event;
+ event.when = ARBITRARY_TIME;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ mMotionAccumulator.process(&event);
+ }
+};
+
+TEST_F(MultiTouchMotionAccumulatorTest, ActiveSlotCountUsingSlotsProtocol) {
+ mMotionAccumulator.configure(*mDeviceContext, SLOT_COUNT, /*usingSlotsProtocol=*/true);
+ // We expect active slot count to match the touches being tracked
+ // first touch
+ processMotionEvent(EV_ABS, ABS_MT_SLOT, 0);
+ processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processMotionEvent(EV_SYN, SYN_REPORT, 0);
+ ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount());
+
+ // second touch
+ processMotionEvent(EV_ABS, ABS_MT_SLOT, 1);
+ processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, 456);
+ processMotionEvent(EV_SYN, SYN_REPORT, 0);
+ ASSERT_EQ(2u, mMotionAccumulator.getActiveSlotsCount());
+
+ // second lifted
+ processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processMotionEvent(EV_SYN, SYN_REPORT, 0);
+ ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount());
+
+ // first lifted
+ processMotionEvent(EV_ABS, ABS_MT_SLOT, 0);
+ processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processMotionEvent(EV_SYN, SYN_REPORT, 0);
+ ASSERT_EQ(0u, mMotionAccumulator.getActiveSlotsCount());
+}
+
+TEST_F(MultiTouchMotionAccumulatorTest, ActiveSlotCountNotUsingSlotsProtocol) {
+ mMotionAccumulator.configure(*mDeviceContext, SLOT_COUNT, /*usingSlotsProtocol=*/false);
+
+ // first touch
+ processMotionEvent(EV_ABS, ABS_MT_POSITION_X, 0);
+ processMotionEvent(EV_ABS, ABS_MT_POSITION_Y, 0);
+ processMotionEvent(EV_SYN, SYN_MT_REPORT, 0);
+ ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount());
+
+ // second touch
+ processMotionEvent(EV_ABS, ABS_MT_POSITION_X, 50);
+ processMotionEvent(EV_ABS, ABS_MT_POSITION_Y, 50);
+ processMotionEvent(EV_SYN, SYN_MT_REPORT, 0);
+ ASSERT_EQ(2u, mMotionAccumulator.getActiveSlotsCount());
+
+ // reset
+ mMotionAccumulator.finishSync();
+ ASSERT_EQ(0u, mMotionAccumulator.getActiveSlotsCount());
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 7237424..68f5857 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -19,16 +19,63 @@
#include <gtest/gtest.h>
#include <vector>
+#include "FakePointerController.h"
+#include "NotifyArgsBuilders.h"
+#include "TestEventMatchers.h"
#include "TestInputListener.h"
namespace android {
+using ControllerType = PointerControllerInterface::ControllerType;
+using testing::AllOf;
+
+namespace {
+
// Helpers to std::visit with lambdas.
template <typename... V>
struct Visitor : V... {};
template <typename... V>
Visitor(V...) -> Visitor<V...>;
+constexpr int32_t DEVICE_ID = 3;
+constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+constexpr int32_t DISPLAY_ID = 5;
+constexpr int32_t ANOTHER_DISPLAY_ID = 10;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+
+const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20);
+const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200);
+const auto SECOND_TOUCH_POINTER = PointerBuilder(/*id=*/1, ToolType::FINGER).x(200).y(300);
+const auto STYLUS_POINTER = PointerBuilder(/*id=*/0, ToolType::STYLUS).x(100).y(200);
+
+static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source,
+ int32_t associatedDisplayId) {
+ InputDeviceIdentifier identifier;
+
+ auto info = InputDeviceInfo();
+ info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias",
+ /*isExternal=*/false, /*hasMic=*/false, associatedDisplayId);
+ info.addSource(source);
+ return info;
+}
+
+static std::vector<DisplayViewport> createViewports(std::vector<int32_t> displayIds) {
+ std::vector<DisplayViewport> viewports;
+ for (auto displayId : displayIds) {
+ DisplayViewport viewport;
+ viewport.displayId = displayId;
+ viewport.logicalRight = DISPLAY_WIDTH;
+ viewport.logicalBottom = DISPLAY_HEIGHT;
+ viewports.push_back(viewport);
+ }
+ return viewports;
+}
+
+} // namespace
+
// --- PointerChoreographerTest ---
class PointerChoreographerTest : public testing::Test, public PointerChoreographerPolicyInterface {
@@ -36,7 +83,55 @@
TestInputListener mTestListener;
PointerChoreographer mChoreographer{mTestListener, *this};
- std::shared_ptr<PointerControllerInterface> createPointerController() { return {}; }
+ std::shared_ptr<FakePointerController> assertPointerControllerCreated(
+ ControllerType expectedType) {
+ EXPECT_TRUE(mLastCreatedController) << "No PointerController was created";
+ auto [type, controller] = std::move(*mLastCreatedController);
+ EXPECT_EQ(expectedType, type);
+ mLastCreatedController.reset();
+ return controller;
+ }
+
+ void assertPointerControllerNotCreated() { ASSERT_EQ(std::nullopt, mLastCreatedController); }
+
+ void assertPointerControllerRemoved(const std::shared_ptr<FakePointerController>& pc) {
+ // Ensure that the code under test is not holding onto this PointerController.
+ // While the policy initially creates the PointerControllers, the PointerChoreographer is
+ // expected to manage their lifecycles. Although we may not want to strictly enforce how
+ // the object is managed, in this case, we need to have a way of ensuring that the
+ // corresponding graphical resources have been released by the PointerController, and the
+ // simplest way of checking for that is to just make sure that the PointerControllers
+ // themselves are released by Choreographer when no longer in use. This check is ensuring
+ // that the reference retained by the test is the last one.
+ ASSERT_EQ(1, pc.use_count()) << "Expected PointerChoreographer to release all references "
+ "to this PointerController";
+ }
+
+ void assertPointerDisplayIdNotified(int32_t displayId) {
+ ASSERT_EQ(displayId, mPointerDisplayIdNotified);
+ mPointerDisplayIdNotified.reset();
+ }
+
+ void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); }
+
+private:
+ std::optional<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
+ mLastCreatedController;
+ std::optional<int32_t> mPointerDisplayIdNotified;
+
+ std::shared_ptr<PointerControllerInterface> createPointerController(
+ ControllerType type) override {
+ EXPECT_FALSE(mLastCreatedController.has_value())
+ << "More than one PointerController created at a time";
+ std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
+ EXPECT_FALSE(pc->isPointerShown());
+ mLastCreatedController = {type, pc};
+ return pc;
+ }
+
+ void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override {
+ mPointerDisplayIdNotified = displayId;
+ }
};
TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
@@ -86,4 +181,873 @@
}
}
+TEST_F(PointerChoreographerTest, WhenMouseIsJustAddedDoesNotCreatePointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenMouseEventOccursCreatesPointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
+}
+
+TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+
+ // Remove the mouse.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenKeyboardIsAddedDoesNotCreatePointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE)}});
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) {
+ // Just adding a viewport or device should not create a PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ assertPointerControllerNotCreated();
+
+ // After the mouse emits event, PointerController will be created and viewport will be set.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) {
+ // Without viewport information, PointerController will be created by a mouse event
+ // but viewport won't be set.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+
+ // After Choreographer gets viewport, PointerController should also have viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // For a mouse event without a target display, default viewport should be set for
+ // the PointerController.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenDefaultMouseDisplayChangesSetsDefaultMouseViewportForPointerController) {
+ // Set one display as a default mouse display and emit mouse event to create PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, firstDisplayPc->getDisplayId());
+
+ // Change default mouse display. Existing PointerController should be removed.
+ mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerControllerRemoved(firstDisplayPc);
+ assertPointerControllerNotCreated();
+
+ // New PointerController for the new default display will be created by the motion event.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, secondDisplayPc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
+
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotNotified();
+
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointerDisplayIdChanged) {
+ // Add two viewports.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+
+ // Set one viewport as a default mouse display ID.
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+
+ // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified
+ // before a mouse event.
+ mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerControllerRemoved(firstDisplayPc);
+
+ // After a mouse event, pointer display ID will be notified with new default mouse display.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set bounds and initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ pc->assertPosition(110, 220);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
+}
+
+TEST_F(PointerChoreographerTest,
+ AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
+ // Add two displays and set one to default.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // Add two devices, one unassociated and the other associated with non-default mouse display.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+
+ // Set bounds and initial position for PointerControllers.
+ unassociatedMousePc->setPosition(100, 200);
+ associatedMousePc->setPosition(300, 400);
+
+ // Make NotifyMotionArgs from the associated mouse and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // Check the status of the PointerControllers.
+ unassociatedMousePc->assertPosition(100, 200);
+ ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+ associatedMousePc->assertPosition(310, 420);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+ ASSERT_TRUE(associatedMousePc->isPointerShown());
+
+ // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID),
+ WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420)));
+}
+
+TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set bounds and initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Assume that pointer capture is enabled.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/1,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE_RELATIVE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyPointerCaptureChanged(
+ NotifyPointerCaptureChangedArgs(/*id=*/2, systemTime(SYSTEM_TIME_MONOTONIC),
+ PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+
+ // Notify motion as if pointer capture is enabled.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE_RELATIVE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .x(10)
+ .y(20)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20))
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that there's no update on the PointerController.
+ pc->assertPosition(100, 200);
+ ASSERT_FALSE(pc->isPointerShown());
+
+ // Check x-y cooridnates, displayId and cursor position are not changed.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(10, 20), WithRelativeMotion(10, 20), WithDisplayId(ADISPLAY_ID_NONE),
+ WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION)));
+}
+
+TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set bounds and initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ pc->assertPosition(110, 220);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Enable pointer capture and check if the PointerController hid the pointer.
+ mChoreographer.notifyPointerCaptureChanged(
+ NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
+ PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, WhenShowTouchesEnabledAndDisabledDoesNotCreatePointerController) {
+ // Disable show touches and add a touch device.
+ mChoreographer.setShowTouchesEnabled(false);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ assertPointerControllerNotCreated();
+
+ // Enable show touches. PointerController still should not be created.
+ mChoreographer.setShowTouchesEnabled(true);
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchEventOccursCreatesPointerController) {
+ // Add a touch device and enable show touches.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+
+ // Emit touch event. Now PointerController should be created.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerCreated(ControllerType::TOUCH);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenShowTouchesDisabledAndTouchEventOccursDoesNotCreatePointerController) {
+ // Add a touch device and disable show touches.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(false);
+ assertPointerControllerNotCreated();
+
+ // Emit touch event. Still, PointerController should not be created.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchDeviceIsRemovedRemovesPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+ // Remove the device.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenShowTouchesDisabledRemovesPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+ assertPointerControllerNotCreated();
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+ // Disable show touches.
+ mChoreographer.setShowTouchesEnabled(false);
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpots) {
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+
+ // Emit first pointer down.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ // Emit second pointer down.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .pointer(SECOND_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 2);
+
+ // Emit second pointer up.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_UP |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .pointer(SECOND_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ // Emit first pointer up.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 0);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpotsForStylusEvent) {
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+ DISPLAY_ID)}});
+
+ // Emit down event with stylus properties.
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertSpotCount(DISPLAY_ID, 1);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpotsForTwoDisplays) {
+ mChoreographer.setShowTouchesEnabled(true);
+ // Add two touch devices associated to different displays.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ ANOTHER_DISPLAY_ID)}});
+
+ // Emit touch event with first device.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::TOUCH);
+ firstDisplayPc->assertSpotCount(DISPLAY_ID, 1);
+
+ // Emit touch events with second device.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .pointer(SECOND_TOUCH_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // There should be another PointerController created.
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+ // Check if the spots are set for the second device.
+ secondDisplayPc->assertSpotCount(ANOTHER_DISPLAY_ID, 2);
+
+ // Check if there's no change on the spot of the first device.
+ firstDisplayPc->assertSpotCount(DISPLAY_ID, 1);
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchDeviceIsResetClearsSpots) {
+ // Make sure the PointerController is created and there is a spot.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ // Reset the device and ensure the touch pointer controller was removed.
+ mChoreographer.notifyDeviceReset(NotifyDeviceResetArgs(/*id=*/1, /*eventTime=*/0, DEVICE_ID));
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenStylusPointerIconEnabledAndDisabledDoesNotCreatePointerController) {
+ // Disable stylus pointer icon and add a stylus device.
+ mChoreographer.setStylusPointerIconEnabled(false);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ assertPointerControllerNotCreated();
+
+ // Enable stylus pointer icon. PointerController still should not be created.
+ mChoreographer.setStylusPointerIconEnabled(true);
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusHoverEventOccursCreatesPointerController) {
+ // Add a stylus device and enable stylus pointer icon.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ assertPointerControllerNotCreated();
+
+ // Emit hover event. Now PointerController should be created.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerCreated(ControllerType::STYLUS);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenStylusPointerIconDisabledAndHoverEventOccursDoesNotCreatePointerController) {
+ // Add a stylus device and disable stylus pointer icon.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(false);
+ assertPointerControllerNotCreated();
+
+ // Emit hover event. Still, PointerController should not be created.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusDeviceIsRemovedRemovesPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Remove the device.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusPointerIconDisabledRemovesPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Disable stylus pointer icon.
+ mChoreographer.setStylusPointerIconEnabled(false);
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, SetsViewportForStylusPointerController) {
+ // Set viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Check that displayId is set.
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Check that displayId is unset.
+ ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+
+ // Set viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Check that displayId is set.
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenViewportDoesNotMatchDoesNotSetViewportForStylusPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Check that displayId is unset.
+ ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+
+ // Set viewport which does not match the associated display of the stylus.
+ mChoreographer.setDisplayViewports(createViewports({ANOTHER_DISPLAY_ID}));
+
+ // Check that displayId is still unset.
+ ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) {
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Emit hover enter event. This is for creating PointerController.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Emit hover move event. After bounds are set, PointerController will update the position.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertPosition(150, 250);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Emit hover exit event.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ // Check that the pointer is gone.
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointerForTwoDisplays) {
+ mChoreographer.setStylusPointerIconEnabled(true);
+ // Add two stylus devices associated to different displays.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, ANOTHER_DISPLAY_ID)}});
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+
+ // Emit hover event with first device. This is for creating PointerController.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Emit hover event with second device. This is for creating PointerController.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // There should be another PointerController created.
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Emit hover event with first device.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // Check the pointer of the first device.
+ firstDisplayPc->assertPosition(150, 250);
+ ASSERT_TRUE(firstDisplayPc->isPointerShown());
+
+ // Emit hover event with second device.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(250).y(350))
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
+
+ // Check the pointer of the second device.
+ secondDisplayPc->assertPosition(250, 350);
+ ASSERT_TRUE(secondDisplayPc->isPointerShown());
+
+ // Check that there's no change on the pointer of the first device.
+ firstDisplayPc->assertPosition(150, 250);
+ ASSERT_TRUE(firstDisplayPc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusDeviceIsResetRemovesPointer) {
+ // Make sure the PointerController is created and there is a pointer.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Reset the device and see the pointer controller was removed.
+ mChoreographer.notifyDeviceReset(NotifyDeviceResetArgs(/*id=*/1, /*eventTime=*/0, DEVICE_ID));
+ assertPointerControllerRemoved(pc);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index ee6ff53..a0a0f5a 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -464,9 +464,32 @@
return WithPointersMatcher(pointers);
}
-MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
- *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
- return arg.keyCode == keyCode;
+/// Key code
+class WithKeyCodeMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithKeyCodeMatcher(int32_t keyCode) : mKeyCode(keyCode) {}
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mKeyCode == args.keyCode;
+ }
+
+ bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+ return mKeyCode == event.getKeyCode();
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with key code " << KeyEvent::getLabel(mKeyCode);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong key code"; }
+
+private:
+ const int32_t mKeyCode;
+};
+
+inline WithKeyCodeMatcher WithKeyCode(int32_t keyCode) {
+ return WithKeyCodeMatcher(keyCode);
}
MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 9313a89..8a4f6f0 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -167,3 +167,17 @@
"LatencyTrackerFuzzer.cpp",
],
}
+
+cc_fuzz {
+ name: "inputflinger_input_dispatcher_fuzzer",
+ defaults: [
+ "inputflinger_fuzz_defaults",
+ "libinputdispatcher_defaults",
+ ],
+ shared_libs: [
+ "libinputreporter",
+ ],
+ srcs: [
+ "InputDispatcherFuzzer.cpp",
+ ],
+}
diff --git a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
new file mode 100644
index 0000000..885820f
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+namespace {
+static constexpr int32_t MAX_RANDOM_POINTERS = 4;
+static constexpr int32_t MAX_RANDOM_DEVICES = 4;
+} // namespace
+
+int getFuzzedMotionAction(FuzzedDataProvider& fdp) {
+ int actionMasked = fdp.PickValueInArray<int>({
+ AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE,
+ AMOTION_EVENT_ACTION_HOVER_ENTER, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AMOTION_EVENT_ACTION_HOVER_EXIT, AMOTION_EVENT_ACTION_CANCEL,
+ // do not inject AMOTION_EVENT_ACTION_OUTSIDE,
+ AMOTION_EVENT_ACTION_SCROLL, AMOTION_EVENT_ACTION_POINTER_DOWN,
+ AMOTION_EVENT_ACTION_POINTER_UP,
+ // do not send buttons until verifier supports them
+ // AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ // AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ });
+ switch (actionMasked) {
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+ const int32_t index = fdp.ConsumeIntegralInRange(0, MAX_RANDOM_POINTERS - 1);
+ const int32_t action =
+ actionMasked | (index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ return action;
+ }
+ default:
+ return actionMasked;
+ }
+}
+
+/**
+ * For now, focus on the 3 main sources.
+ */
+int getFuzzedSource(FuzzedDataProvider& fdp) {
+ return fdp.PickValueInArray<int>({
+ // AINPUT_SOURCE_UNKNOWN,
+ // AINPUT_SOURCE_KEYBOARD,
+ // AINPUT_SOURCE_DPAD,
+ // AINPUT_SOURCE_GAMEPAD,
+ AINPUT_SOURCE_TOUCHSCREEN, AINPUT_SOURCE_MOUSE, AINPUT_SOURCE_STYLUS,
+ // AINPUT_SOURCE_BLUETOOTH_STYLUS,
+ // AINPUT_SOURCE_TRACKBALL,
+ // AINPUT_SOURCE_MOUSE_RELATIVE,
+ // AINPUT_SOURCE_TOUCHPAD,
+ // AINPUT_SOURCE_TOUCH_NAVIGATION,
+ // AINPUT_SOURCE_JOYSTICK,
+ // AINPUT_SOURCE_HDMI,
+ // AINPUT_SOURCE_SENSOR,
+ // AINPUT_SOURCE_ROTARY_ENCODER,
+ // AINPUT_SOURCE_ANY,
+ });
+}
+
+int getFuzzedButtonState(FuzzedDataProvider& fdp) {
+ return fdp.PickValueInArray<int>({
+ 0,
+ // AMOTION_EVENT_BUTTON_PRIMARY,
+ // AMOTION_EVENT_BUTTON_SECONDARY,
+ // AMOTION_EVENT_BUTTON_TERTIARY,
+ // AMOTION_EVENT_BUTTON_BACK,
+ // AMOTION_EVENT_BUTTON_FORWARD,
+ // AMOTION_EVENT_BUTTON_STYLUS_PRIMARY,
+ // AMOTION_EVENT_BUTTON_STYLUS_SECONDARY,
+ });
+}
+
+int32_t getFuzzedFlags(FuzzedDataProvider& fdp, int32_t action) {
+ constexpr std::array<int32_t, 4> FLAGS{
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
+ AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE,
+ };
+
+ int32_t flags = 0;
+ for (size_t i = 0; i < fdp.ConsumeIntegralInRange(size_t(0), FLAGS.size()); i++) {
+ flags |= fdp.PickValueInArray<int32_t>(FLAGS);
+ }
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ if (MotionEvent::getActionMasked(action) == AMOTION_EVENT_ACTION_POINTER_UP) {
+ if (fdp.ConsumeBool()) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ }
+ return flags;
+}
+
+int32_t getFuzzedPointerCount(FuzzedDataProvider& fdp, int32_t action) {
+ switch (MotionEvent::getActionMasked(action)) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_UP: {
+ return 1;
+ }
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_MOVE:
+ return fdp.ConsumeIntegralInRange<int32_t>(1, MAX_RANDOM_POINTERS);
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ return 1;
+ case AMOTION_EVENT_ACTION_SCROLL:
+ return 1;
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+ const uint8_t actionIndex = MotionEvent::getActionIndex(action);
+ const int32_t count =
+ std::max(actionIndex + 1,
+ fdp.ConsumeIntegralInRange<int32_t>(1, MAX_RANDOM_POINTERS));
+ // Need to have at least 2 pointers
+ return std::max(2, count);
+ }
+ case AMOTION_EVENT_ACTION_BUTTON_PRESS:
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+ return 1;
+ }
+ }
+ return 1;
+}
+
+ToolType getToolType(int32_t source) {
+ switch (source) {
+ case AINPUT_SOURCE_TOUCHSCREEN:
+ return ToolType::FINGER;
+ case AINPUT_SOURCE_MOUSE:
+ return ToolType::MOUSE;
+ case AINPUT_SOURCE_STYLUS:
+ return ToolType::STYLUS;
+ }
+ return ToolType::UNKNOWN;
+}
+
+inline nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+NotifyMotionArgs generateFuzzedMotionArgs(IdGenerator& idGenerator, FuzzedDataProvider& fdp,
+ int32_t maxDisplays) {
+ // Create a basic motion event for testing
+ const int32_t source = getFuzzedSource(fdp);
+ const ToolType toolType = getToolType(source);
+ const int32_t action = getFuzzedMotionAction(fdp);
+ const int32_t pointerCount = getFuzzedPointerCount(fdp, action);
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+ for (int i = 0; i < pointerCount; i++) {
+ PointerProperties properties{};
+ properties.id = i;
+ properties.toolType = toolType;
+ pointerProperties.push_back(properties);
+
+ PointerCoords coords{};
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, fdp.ConsumeIntegralInRange<int>(-1000, 1000));
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, fdp.ConsumeIntegralInRange<int>(-1000, 1000));
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1);
+ pointerCoords.push_back(coords);
+ }
+
+ const int32_t displayId = fdp.ConsumeIntegralInRange<int32_t>(0, maxDisplays - 1);
+ const int32_t deviceId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DEVICES - 1);
+
+ // Current time +- 5 seconds
+ const nsecs_t currentTime = now();
+ const nsecs_t downTime =
+ fdp.ConsumeIntegralInRange<nsecs_t>(currentTime - 5E9, currentTime + 5E9);
+ const nsecs_t readTime = downTime;
+ const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(downTime, downTime + 1E9);
+
+ const float cursorX = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
+ const float cursorY = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
+ return NotifyMotionArgs(idGenerator.nextId(), eventTime, readTime, deviceId, source, displayId,
+ POLICY_FLAG_PASS_TO_USER, action,
+ /*actionButton=*/fdp.ConsumeIntegral<int32_t>(),
+ getFuzzedFlags(fdp, action), AMETA_NONE, getFuzzedButtonState(fdp),
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+ pointerProperties.data(), pointerCoords.data(),
+ /*xPrecision=*/0,
+ /*yPrecision=*/0, cursorX, cursorY, downTime,
+ /*videoFrames=*/{});
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
index 3b3ed9b..deb811d 100644
--- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -16,44 +16,16 @@
#include <MapperHelpers.h>
#include <fuzzer/FuzzedDataProvider.h>
+#include "FuzzedInputStream.h"
#include "InputCommonConverter.h"
#include "InputProcessor.h"
namespace android {
-static constexpr int32_t MAX_AXES = 64;
+namespace {
-// Used by two fuzz operations and a bit lengthy, so pulled out into a function.
-NotifyMotionArgs generateFuzzedMotionArgs(FuzzedDataProvider &fdp) {
- // Create a basic motion event for testing
- PointerProperties properties;
- properties.id = 0;
- properties.toolType = getFuzzedToolType(fdp);
- PointerCoords coords;
- coords.clear();
- for (int32_t i = 0; i < fdp.ConsumeIntegralInRange<int32_t>(0, MAX_AXES); i++) {
- coords.setAxisValue(fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeFloatingPoint<float>());
- }
+constexpr int32_t MAX_RANDOM_DISPLAYS = 4;
- const nsecs_t downTime = 2;
- const nsecs_t readTime = downTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8);
- NotifyMotionArgs motionArgs(/*sequenceNum=*/fdp.ConsumeIntegral<uint32_t>(),
- /*eventTime=*/downTime, readTime,
- /*deviceId=*/fdp.ConsumeIntegral<int32_t>(), AINPUT_SOURCE_ANY,
- ADISPLAY_ID_DEFAULT,
- /*policyFlags=*/fdp.ConsumeIntegral<uint32_t>(),
- AMOTION_EVENT_ACTION_DOWN,
- /*actionButton=*/fdp.ConsumeIntegral<int32_t>(),
- /*flags=*/fdp.ConsumeIntegral<int32_t>(), AMETA_NONE,
- /*buttonState=*/fdp.ConsumeIntegral<int32_t>(),
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
- /*pointerCount=*/1, &properties, &coords,
- /*xPrecision=*/fdp.ConsumeFloatingPoint<float>(),
- /*yPrecision=*/fdp.ConsumeFloatingPoint<float>(),
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
- /*videoFrames=*/{});
- return motionArgs;
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
@@ -62,6 +34,7 @@
std::unique_ptr<FuzzInputListener> mFuzzListener = std::make_unique<FuzzInputListener>();
std::unique_ptr<InputProcessorInterface> mClassifier =
std::make_unique<InputProcessor>(*mFuzzListener);
+ IdGenerator idGenerator(IdGenerator::Source::OTHER);
while (fdp.remaining_bytes() > 0) {
fdp.PickValueInArray<std::function<void()>>({
@@ -90,7 +63,8 @@
},
[&]() -> void {
// SendToNextStage_NotifyMotionArgs
- mClassifier->notifyMotion(generateFuzzedMotionArgs(fdp));
+ mClassifier->notifyMotion(
+ generateFuzzedMotionArgs(idGenerator, fdp, MAX_RANDOM_DISPLAYS));
},
[&]() -> void {
// SendToNextStage_NotifySwitchArgs
@@ -108,7 +82,8 @@
},
[&]() -> void {
// InputClassifierConverterTest
- const NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
+ const NotifyMotionArgs motionArgs =
+ generateFuzzedMotionArgs(idGenerator, fdp, MAX_RANDOM_DISPLAYS);
aidl::android::hardware::input::common::MotionEvent motionEvent =
notifyMotionArgsToHalMotionEvent(motionArgs);
},
diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
new file mode 100644
index 0000000..214649c
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "../FakeApplicationHandle.h"
+#include "../FakeInputDispatcherPolicy.h"
+#include "../FakeWindowHandle.h"
+#include "FuzzedInputStream.h"
+#include "dispatcher/InputDispatcher.h"
+#include "input/InputVerifier.h"
+
+namespace android {
+
+using android::base::Result;
+using android::gui::WindowInfo;
+
+namespace inputdispatcher {
+
+namespace {
+
+static constexpr int32_t MAX_RANDOM_DISPLAYS = 4;
+static constexpr int32_t MAX_RANDOM_WINDOWS = 4;
+
+/**
+ * Provide a valid motion stream, to make the fuzzer more effective.
+ */
+class NotifyStreamProvider {
+public:
+ NotifyStreamProvider(FuzzedDataProvider& fdp)
+ : mFdp(fdp), mIdGenerator(IdGenerator::Source::OTHER), mVerifier("Fuzz verifier") {}
+
+ std::optional<NotifyMotionArgs> nextMotion() {
+ NotifyMotionArgs args = generateFuzzedMotionArgs(mIdGenerator, mFdp, MAX_RANDOM_DISPLAYS);
+ const Result<void> result =
+ mVerifier.processMovement(args.deviceId, args.source, args.action,
+ args.getPointerCount(), args.pointerProperties.data(),
+ args.pointerCoords.data(), args.flags);
+ if (result.ok()) {
+ return args;
+ }
+ return {};
+ }
+
+private:
+ FuzzedDataProvider& mFdp;
+
+ IdGenerator mIdGenerator;
+
+ InputVerifier mVerifier;
+};
+
+} // namespace
+
+sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatcher& dispatcher,
+ int32_t displayId) {
+ static size_t windowNumber = 0;
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ std::string windowName = android::base::StringPrintf("Win") + std::to_string(windowNumber++);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, dispatcher, "Fake", displayId);
+
+ const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+ const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100);
+
+ window->setFrame(Rect(left, top, left + width, top + height));
+ window->setSlippery(fdp.ConsumeBool());
+ window->setDupTouchToWallpaper(fdp.ConsumeBool());
+ window->setTrustedOverlay(fdp.ConsumeBool());
+ return window;
+}
+
+void randomizeWindows(
+ std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>>& windowsPerDisplay,
+ FuzzedDataProvider& fdp, InputDispatcher& dispatcher) {
+ const int32_t displayId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DISPLAYS - 1);
+ std::vector<sp<FakeWindowHandle>>& windows = windowsPerDisplay[displayId];
+
+ fdp.PickValueInArray<std::function<void()>>({
+ // Add a new window
+ [&]() -> void {
+ if (windows.size() < MAX_RANDOM_WINDOWS) {
+ windows.push_back(generateFuzzedWindow(fdp, dispatcher, displayId));
+ }
+ },
+ // Remove a window
+ [&]() -> void {
+ if (windows.empty()) {
+ return;
+ }
+ const int32_t erasedPosition =
+ fdp.ConsumeIntegralInRange<int32_t>(0, windows.size() - 1);
+
+ windows.erase(windows.begin() + erasedPosition);
+ if (windows.empty()) {
+ windowsPerDisplay.erase(displayId);
+ }
+ },
+ // Could also clone a window, change flags, reposition, etc...
+ })();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ NotifyStreamProvider streamProvider(fdp);
+
+ FakeInputDispatcherPolicy fakePolicy;
+ InputDispatcher dispatcher(fakePolicy);
+ dispatcher.setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
+ // Start InputDispatcher thread
+ dispatcher.start();
+
+ std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>> windowsPerDisplay;
+
+ // Randomly invoke InputDispatcher api's until randomness is exhausted.
+ while (fdp.remaining_bytes() > 0) {
+ fdp.PickValueInArray<std::function<void()>>({
+ [&]() -> void {
+ std::optional<NotifyMotionArgs> motion = streamProvider.nextMotion();
+ if (motion) {
+ dispatcher.notifyMotion(*motion);
+ }
+ },
+ [&]() -> void {
+ // Scramble the windows we currently have
+ randomizeWindows(/*byref*/ windowsPerDisplay, fdp, dispatcher);
+
+ std::vector<WindowInfo> windowInfos;
+ for (const auto& [displayId, windows] : windowsPerDisplay) {
+ for (const sp<FakeWindowHandle>& window : windows) {
+ windowInfos.emplace_back(*window->getInfo());
+ }
+ }
+
+ dispatcher.onWindowInfosChanged(
+ {windowInfos, {}, /*vsyncId=*/0, /*timestamp=*/0});
+ },
+ // Consume on all the windows
+ [&]() -> void {
+ for (const auto& [_, windows] : windowsPerDisplay) {
+ for (const sp<FakeWindowHandle>& window : windows) {
+ // To speed up the fuzzing, don't wait for consumption. If there's an
+ // event pending, this can be consumed on the next call instead.
+ // We also don't care about whether consumption succeeds here, or what
+ // kind of event is returned.
+ window->consume(0ms);
+ }
+ }
+ },
+ })();
+ }
+
+ dispatcher.stop();
+
+ return 0;
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index bdedfdf..2f84497 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -275,6 +275,7 @@
void clearSpots() override {}
int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); }
void setDisplayViewport(const DisplayViewport& displayViewport) override {}
+ std::string dump() override { return ""; }
};
class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
@@ -309,6 +310,10 @@
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); }
+ std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
+ int32_t associatedDisplayId) override {
+ return {};
+ }
};
class FuzzInputListener : public virtual InputListenerInterface {
@@ -360,6 +365,12 @@
void setPreventingTouchpadTaps(bool prevent) {}
bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
+
+ void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; };
+ nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; };
+
+private:
+ nsecs_t mLastKeyDownTimestamp;
};
template <class Fdp>
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 8b16890..1f72e8b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -19,6 +19,7 @@
"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 0000000..ef723c2
--- /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 0000000..99b5b8b
--- /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 9d0f285..17fa7be 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -245,6 +245,7 @@
],
static_libs: [
"android.frameworks.displayservice@1.0",
+ "libc++fs",
"libdisplayservicehidl",
"libserviceutils",
],
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 1a8644e..1a235e9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -96,6 +96,9 @@
// The expected time for the next present
nsecs_t expectedPresentTime{0};
+ // The frameInterval for the next present
+ Fps frameInterval{};
+
// If set, a frame has been scheduled for that time.
std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 370c7cf..40ebf44 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -298,14 +298,14 @@
virtual void finishFrame(GpuCompositionResult&&) = 0;
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
- virtual void postFramebuffer() = 0;
+ virtual void presentFrameAndReleaseLayers() = 0;
virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
virtual bool chooseCompositionStrategy(
std::optional<android::HWComposer::DeviceRequestedChanges>*) = 0;
virtual void applyCompositionStrategy(
const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0;
virtual bool getSkipColorTransform() const = 0;
- virtual FrameFences presentAndGetFrameFences() = 0;
+ virtual FrameFences presentFrame() = 0;
virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
std::vector<LayerFE*> &outLayerRef) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 6cf1d68..de82931 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -59,7 +59,7 @@
std::optional<android::HWComposer::DeviceRequestedChanges>*) override;
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override;
bool getSkipColorTransform() const override;
- compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+ compositionengine::Output::FrameFences presentFrame() override;
void setExpensiveRenderingExpected(bool) override;
void finishFrame(GpuCompositionResult&&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 229a657..d95fbea 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -102,7 +102,7 @@
std::optional<base::unique_fd> composeSurfaces(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&) override;
- void postFramebuffer() override;
+ void presentFrameAndReleaseLayers() override;
void renderCachedSets(const CompositionRefreshArgs&) override;
void cacheClientCompositionRequests(uint32_t) override;
bool canPredictCompositionStrategy(const CompositionRefreshArgs&) override;
@@ -133,7 +133,7 @@
};
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
- compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+ compositionengine::Output::FrameFences presentFrame() override;
virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings() const;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 6cb1e7e..692ed24 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -127,6 +127,9 @@
// The expected time for the next present
nsecs_t expectedPresentTime{0};
+ // The frameInterval for the next present
+ Fps frameInterval{};
+
// Current display brightness
float displayBrightnessNits{-1.f};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index a56fc79..c88fbd6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -118,9 +118,9 @@
base::unique_fd&));
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
- MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
- MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+ MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<compositionengine::LayerFE*>&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index f2acfc9..469fb38 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -255,10 +255,10 @@
const TimePoint hwcValidateStartTime = TimePoint::now();
- if (status_t result =
- hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
- getState().earliestPresentTime,
- getState().expectedPresentTime, outChanges);
+ if (status_t result = hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
+ getState().earliestPresentTime,
+ getState().expectedPresentTime,
+ getState().frameInterval, outChanges);
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
@@ -362,8 +362,8 @@
static_cast<ui::PixelFormat>(clientTargetProperty.clientTargetProperty.pixelFormat));
}
-compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
- auto fences = impl::Output::presentAndGetFrameFences();
+compositionengine::Output::FrameFences Display::presentFrame() {
+ auto fences = impl::Output::presentFrame();
const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
if (mIsDisconnected || !halDisplayIdOpt) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index fa3733b..2ae80de 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -448,7 +448,7 @@
devOptRepaintFlash(refreshArgs);
finishFrame(std::move(result));
- postFramebuffer();
+ presentFrameAndReleaseLayers();
renderCachedSets(refreshArgs);
}
@@ -836,6 +836,7 @@
editState().earliestPresentTime = refreshArgs.earliestPresentTime;
editState().expectedPresentTime = refreshArgs.expectedPresentTime;
+ editState().frameInterval = refreshArgs.frameInterval;
editState().powerCallback = refreshArgs.powerCallback;
compositionengine::OutputLayer* peekThroughLayer = nullptr;
@@ -1152,7 +1153,7 @@
}
}
- postFramebuffer();
+ presentFrameAndReleaseLayers();
std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay);
@@ -1509,7 +1510,7 @@
return false;
}
-void Output::postFramebuffer() {
+void Output::presentFrameAndReleaseLayers() {
ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
@@ -1520,7 +1521,7 @@
auto& outputState = editState();
outputState.dirtyRegion.clear();
- auto frame = presentAndGetFrameFences();
+ auto frame = presentFrame();
mRenderSurface->onPresentDisplayCompleted();
@@ -1590,7 +1591,7 @@
return true;
}
-compositionengine::Output::FrameFences Output::presentAndGetFrameFences() {
+compositionengine::Output::FrameFences Output::presentFrame() {
compositionengine::Output::FrameFences result;
if (getState().usesClientComposition) {
result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence();
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 027004a..4a778d4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -582,7 +582,7 @@
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _))
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _))
.WillOnce(Return(INVALID_OPERATION));
chooseCompositionStrategy(mDisplay.get());
@@ -606,8 +606,8 @@
.WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
- .WillOnce(testing::DoAll(testing::SetArgPointee<4>(mDeviceRequestedChanges),
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
+ .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
Return(NO_ERROR)));
EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
.Times(1);
@@ -659,8 +659,8 @@
.WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(mDeviceRequestedChanges), Return(NO_ERROR)));
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<5>(mDeviceRequestedChanges), Return(NO_ERROR)));
EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
.Times(1);
EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
@@ -867,7 +867,7 @@
}
/*
- * Display::presentAndGetFrameFences()
+ * Display::presentFrame()
*/
using DisplayPresentAndGetFrameFencesTest = DisplayWithLayersTestCommon;
@@ -876,7 +876,7 @@
auto args = getDisplayCreationArgsForGpuVirtualDisplay();
auto gpuDisplay{impl::createDisplay(mCompositionEngine, args)};
- auto result = gpuDisplay->presentAndGetFrameFences();
+ auto result = gpuDisplay->presentFrame();
ASSERT_TRUE(result.presentFence.get());
EXPECT_FALSE(result.presentFence->isValid());
@@ -900,7 +900,7 @@
.WillOnce(Return(layer2Fence));
EXPECT_CALL(mHwComposer, clearReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
- auto result = mDisplay->presentAndGetFrameFences();
+ auto result = mDisplay->presentFrame();
EXPECT_EQ(presentFence, result.presentFence);
@@ -1060,7 +1060,7 @@
}
};
-TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
+TEST_F(DisplayFunctionalTest, presentFrameAndReleaseLayersCriticalCallsAreOrdered) {
InSequence seq;
mDisplay->editState().isEnabled = true;
@@ -1068,7 +1068,7 @@
EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
- mDisplay->postFramebuffer();
+ mDisplay->presentFrameAndReleaseLayers();
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 6807c8e..b2491d8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -54,9 +54,9 @@
MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
- MOCK_METHOD5(getDeviceCompositionChanges,
- status_t(HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>,
- nsecs_t, std::optional<android::HWComposer::DeviceRequestedChanges>*));
+ MOCK_METHOD(status_t, getDeviceCompositionChanges,
+ (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
+ Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD5(setClientTarget,
status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
ui::Dataspace));
@@ -148,7 +148,7 @@
getOverlaySupport, (), (const, override));
MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
MOCK_METHOD(status_t, notifyExpectedPresentIfRequired,
- (PhysicalDisplayId, nsecs_t, int32_t, int32_t));
+ (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index cdcb68d..5537fcd 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2007,7 +2007,7 @@
MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
- MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
};
@@ -2029,7 +2029,7 @@
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(_));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
@@ -2049,7 +2049,7 @@
EXPECT_CALL(mOutput, prepareFrameAsync());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(_));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
@@ -2895,7 +2895,7 @@
std::optional<base::unique_fd>(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&));
- MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
@@ -2932,7 +2932,7 @@
mOutput.mState.isEnabled = false;
InSequence seq;
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2944,7 +2944,7 @@
InSequence seq;
EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2960,7 +2960,7 @@
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2978,7 +2978,7 @@
std::optional<base::unique_fd>(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&));
- MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
@@ -3057,14 +3057,14 @@
}
/*
- * Output::postFramebuffer()
+ * Output::presentFrameAndReleaseLayers()
*/
struct OutputPostFramebufferTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+ MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
};
struct Layer {
@@ -3104,7 +3104,7 @@
TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
mOutput.mState.isEnabled = false;
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
@@ -3119,10 +3119,10 @@
// setup below are satisfied in the specific order.
InSequence seq;
- EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
@@ -3141,7 +3141,7 @@
frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
- EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
// Compare the pointers values of each fence to make sure the correct ones
@@ -3164,7 +3164,7 @@
EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
});
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
@@ -3177,7 +3177,7 @@
frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
- EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
// Fence::merge is called, and since none of the fences are actually valid,
@@ -3187,7 +3187,7 @@
EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
@@ -3212,7 +3212,7 @@
Output::FrameFences frameFences;
frameFences.presentFence = presentFence;
- EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
// Each released layer should be given the presentFence.
@@ -3232,7 +3232,7 @@
EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
});
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
// After the call the list of released layers should have been cleared.
EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 00590e6..d9318af 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_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 @@
};
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 5b6591a..2ffe92b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -63,13 +63,14 @@
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 a044534..a40f310 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -269,6 +269,11 @@
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 @@
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 1643ad0..317f2b0 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -19,6 +19,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "AidlComposerHal.h"
+#include "FlagManager.h"
#include <SurfaceFlingerProperties.h>
#include <android-base/file.h>
@@ -281,7 +282,7 @@
}
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::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 @@
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::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 @@
auto reader = getReader(display);
if (writer && reader) {
writer->get().presentOrvalidateDisplay(displayId,
- ClockMonotonicTimestamp{expectedPresentTime});
+ ClockMonotonicTimestamp{expectedPresentTime},
+ frameIntervalNs);
error = execute(display);
} else {
error = Error::BAD_DISPLAY;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 7693a80..1635a16 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -134,12 +134,13 @@
Error setClientTargetSlotCount(Display display) override;
- Error validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests) override;
+ Error validateDisplay(Display display, nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) override;
Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) override;
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) override;
Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
/* see setClientTarget for the purpose of slot */
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 6704d88..082717a 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -174,11 +174,13 @@
virtual Error setClientTargetSlotCount(Display display) = 0;
virtual Error validateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests) = 0;
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) = 0;
virtual Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) = 0;
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) = 0;
virtual Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) = 0;
/* see setClientTarget for the purpose of slot */
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 1775a7a..f32fb3a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -30,8 +30,8 @@
#include <scheduler/Fps.h>
#include "DisplayHardware/Hal.h"
+#include "FlagManager.h"
#include "Scheduler/StrongTyping.h"
-#include "Utils/FlagUtils.h"
namespace android {
@@ -50,7 +50,6 @@
using DisplayModes = ftl::SmallMap<DisplayModeId, DisplayModePtr, 3>;
using DisplayModeIterator = DisplayModes::const_iterator;
-using namespace com::android::graphics::surfaceflinger;
class DisplayMode {
public:
@@ -140,7 +139,7 @@
// Peak refresh rate represents the highest refresh rate that can be used
// for the presentation.
Fps getPeakFps() const {
- return flagutils::vrrConfigEnabled() && mVrrConfig
+ return FlagManager::getInstance().vrr_config() && mVrrConfig
? Fps::fromPeriodNsecs(mVrrConfig->minFrameIntervalNs)
: mVsyncRate;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 0c2b77d..bc763b2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -517,11 +517,12 @@
return static_cast<Error>(intError);
}
-Error Display::validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+Error Display::validate(nsecs_t expectedPresentTime, int32_t frameIntervalNs, uint32_t* outNumTypes,
uint32_t* outNumRequests) {
uint32_t numTypes = 0;
uint32_t numRequests = 0;
- auto intError = mComposer.validateDisplay(mId, expectedPresentTime, &numTypes, &numRequests);
+ auto intError = mComposer.validateDisplay(mId, expectedPresentTime, frameIntervalNs, &numTypes,
+ &numRequests);
auto error = static_cast<Error>(intError);
if (error != Error::NONE && !hasChangesError(error)) {
return error;
@@ -532,14 +533,15 @@
return error;
}
-Error Display::presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests, sp<android::Fence>* outPresentFence,
- uint32_t* state) {
+Error Display::presentOrValidate(nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
+ sp<android::Fence>* outPresentFence, uint32_t* state) {
uint32_t numTypes = 0;
uint32_t numRequests = 0;
int32_t presentFenceFd = -1;
- auto intError = mComposer.presentOrValidateDisplay(mId, expectedPresentTime, &numTypes,
- &numRequests, &presentFenceFd, state);
+ auto intError =
+ mComposer.presentOrValidateDisplay(mId, expectedPresentTime, frameIntervalNs, &numTypes,
+ &numRequests, &presentFenceFd, state);
auto error = static_cast<Error>(intError);
if (error != Error::NONE && !hasChangesError(error)) {
return error;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 23dd3e5..e7f807f 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -148,9 +148,10 @@
const android::sp<android::Fence>& releaseFence) = 0;
[[nodiscard]] virtual hal::Error setPowerMode(hal::PowerMode mode) = 0;
[[nodiscard]] virtual hal::Error setVsyncEnabled(hal::Vsync enabled) = 0;
- [[nodiscard]] virtual hal::Error validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests) = 0;
+ [[nodiscard]] virtual hal::Error validate(nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) = 0;
[[nodiscard]] virtual hal::Error presentOrValidate(nsecs_t expectedPresentTime,
+ int32_t frameIntervalNs,
uint32_t* outNumTypes,
uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence,
@@ -233,10 +234,10 @@
const android::sp<android::Fence>& releaseFence) override;
hal::Error setPowerMode(hal::PowerMode) override;
hal::Error setVsyncEnabled(hal::Vsync enabled) override;
- hal::Error validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+ hal::Error validate(nsecs_t expectedPresentTime, int32_t frameIntervalNs, uint32_t* outNumTypes,
uint32_t* outNumRequests) override;
- hal::Error presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests,
+ hal::Error presentOrValidate(nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence,
uint32_t* state) override;
ftl::Future<hal::Error> setDisplayBrightness(
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index fb6089d..6be57d4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -78,6 +78,59 @@
using aidl::android::hardware::graphics::composer3::DisplayCapability;
namespace hal = android::hardware::graphics::composer::hal;
+namespace {
+bool isFrameIntervalOnCadence(android::TimePoint expectedPresentTime,
+ android::TimePoint lastExpectedPresentTimestamp,
+ android::Fps lastFrameInterval, android::Period timeout,
+ android::Duration threshold) {
+ if (lastFrameInterval.getPeriodNsecs() == 0) {
+ return false;
+ }
+
+ const auto expectedPresentTimeDeltaNs =
+ expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns();
+
+ if (expectedPresentTimeDeltaNs > timeout.ns()) {
+ return false;
+ }
+
+ const auto expectedPresentPeriods = static_cast<nsecs_t>(
+ std::round(static_cast<float>(expectedPresentTimeDeltaNs) /
+ static_cast<float>(lastFrameInterval.getPeriodNsecs())));
+ const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods;
+ const auto calculatedExpectedPresentTimeNs =
+ lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs;
+ const auto presentTimeDelta =
+ std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs);
+ return presentTimeDelta < threshold.ns();
+}
+
+bool isExpectedPresentWithinTimeout(android::TimePoint expectedPresentTime,
+ android::TimePoint lastExpectedPresentTimestamp,
+ std::optional<android::Period> timeoutOpt,
+ android::Duration threshold) {
+ if (!timeoutOpt) {
+ // Always within timeout if timeoutOpt is absent and don't send hint
+ // for the timeout
+ return true;
+ }
+
+ if (timeoutOpt->ns() == 0) {
+ // Always outside timeout if timeoutOpt is 0 and always send
+ // the hint for the timeout.
+ return false;
+ }
+
+ if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) {
+ return true;
+ }
+
+ // Check if within the threshold as it can be just outside the timeout
+ return std::abs(expectedPresentTime.ns() -
+ (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns();
+}
+} // namespace
+
namespace android {
HWComposer::~HWComposer() = default;
@@ -445,7 +498,7 @@
status_t HWComposer::getDeviceCompositionChanges(
HalDisplayId displayId, bool frameUsesClientComposition,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
- nsecs_t expectedPresentTime,
+ nsecs_t expectedPresentTime, Fps frameInterval,
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
ATRACE_CALL();
@@ -485,12 +538,17 @@
}();
displayData.validateWasSkipped = false;
- displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+ {
+ std::scoped_lock lock{displayData.expectedPresentLock};
+ displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime);
+ // TODO(b/296636176) Update displayData.lastFrameInterval for present display commands
+ }
+
if (canSkipValidate) {
- sp<Fence> outPresentFence;
+ sp<Fence> outPresentFence = Fence::NO_FENCE;
uint32_t state = UINT32_MAX;
- error = hwcDisplay->presentOrValidate(expectedPresentTime, &numTypes, &numRequests,
- &outPresentFence, &state);
+ error = hwcDisplay->presentOrValidate(expectedPresentTime, frameInterval.getPeriodNsecs(),
+ &numTypes, &numRequests, &outPresentFence, &state);
if (!hasChangesError(error)) {
RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR);
}
@@ -505,7 +563,8 @@
}
// Present failed but Validate ran.
} else {
- error = hwcDisplay->validate(expectedPresentTime, &numTypes, &numRequests);
+ error = hwcDisplay->validate(expectedPresentTime, frameInterval.getPeriodNsecs(), &numTypes,
+ &numRequests);
}
ALOGV("SkipValidate failed, Falling back to SLOW validate/present");
if (!hasChangesError(error)) {
@@ -879,21 +938,44 @@
}
status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
- nsecs_t expectedPresentTime,
- int32_t frameIntervalNs, int32_t timeoutNs) {
+ Period vsyncPeriod,
+ TimePoint expectedPresentTime,
+ Fps frameInterval,
+ std::optional<Period> timeoutOpt) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
-
auto& displayData = mDisplayData[displayId];
- if (expectedPresentTime >= displayData.lastExpectedPresentTimestamp &&
- expectedPresentTime < displayData.lastExpectedPresentTimestamp + timeoutNs) {
- return NO_ERROR;
- }
+ {
+ std::scoped_lock lock{displayData.expectedPresentLock};
+ const auto lastExpectedPresentTimestamp = displayData.lastExpectedPresentTimestamp;
+ const auto lastFrameInterval = displayData.lastFrameInterval;
+ displayData.lastFrameInterval = frameInterval;
+ const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2);
- displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+ const constexpr nsecs_t kOneSecondNs =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ const bool frameIntervalIsOnCadence =
+ isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp,
+ lastFrameInterval,
+ Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0
+ ? timeoutOpt->ns()
+ : kOneSecondNs),
+ threshold);
+
+ const bool expectedPresentWithinTimeout =
+ isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
+ timeoutOpt, threshold);
+
+ if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+ return NO_ERROR;
+ }
+
+ displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+ }
ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__,
- expectedPresentTime, frameIntervalNs);
+ expectedPresentTime, frameInterval.getPeriodNsecs());
const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(),
- expectedPresentTime, frameIntervalNs);
+ expectedPresentTime.ns(),
+ frameInterval.getPeriodNsecs());
if (error != hal::Error::NONE) {
ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str());
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 726a8ea..5846c07 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -147,7 +147,8 @@
virtual status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
- nsecs_t expectedPresentTime, std::optional<DeviceRequestedChanges>* outChanges) = 0;
+ nsecs_t expectedPresentTime, Fps frameInterval,
+ std::optional<DeviceRequestedChanges>* outChanges) = 0;
virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
@@ -301,9 +302,10 @@
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) = 0;
virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
- virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime,
- int32_t frameIntervalNs,
- int32_t timeoutNs) = 0;
+ virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
+ TimePoint expectedPresentTime,
+ Fps frameInterval,
+ std::optional<Period> timeoutOpt) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -346,7 +348,7 @@
status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
- nsecs_t expectedPresentTime,
+ nsecs_t expectedPresentTime, Fps frameInterval,
std::optional<DeviceRequestedChanges>* outChanges) override;
status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
@@ -462,8 +464,9 @@
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) override;
status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
- status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime,
- int32_t frameIntervalNs, int32_t timeoutNs) override;
+ status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
+ TimePoint expectedPresentTime, Fps frameInterval,
+ std::optional<Period> timeoutOpt) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
@@ -497,7 +500,10 @@
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
nsecs_t lastPresentTimestamp = 0;
- nsecs_t lastExpectedPresentTimestamp = 0;
+ std::mutex expectedPresentLock;
+ TimePoint lastExpectedPresentTimestamp GUARDED_BY(expectedPresentLock) =
+ TimePoint::fromNs(0);
+ Fps lastFrameInterval GUARDED_BY(expectedPresentLock) = Fps::fromValue(0);
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index c13e568..1e7c6da 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -665,7 +665,8 @@
}
Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
- uint32_t* outNumTypes, uint32_t* outNumRequests) {
+ int32_t /*frameIntervalNs*/, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
ATRACE_NAME("HwcValidateDisplay");
mWriter.selectDisplay(display);
mWriter.validateDisplay();
@@ -681,8 +682,9 @@
}
Error HidlComposer::presentOrValidateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) {
+ int32_t /*frameIntervalNs*/, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) {
ATRACE_NAME("HwcPresentOrValidateDisplay");
mWriter.selectDisplay(display);
mWriter.presentOrvalidateDisplay();
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 1004ddd..5c19b47 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -236,12 +236,13 @@
Error setClientTargetSlotCount(Display display) override;
- Error validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes,
- uint32_t* outNumRequests) override;
+ Error validateDisplay(Display display, nsecs_t expectedPresentTime, int32_t frameIntervalNs,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) override;
Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
- uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) override;
+ int32_t frameIntervalNs, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) override;
Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
/* see setClientTarget for the purpose of slot */
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index f00ef67..e005ad3 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -251,7 +251,12 @@
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/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
index f8ad8f6..13b8a6c 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -26,37 +26,21 @@
#include <server_configurable_flags/get_flags.h>
#include <cinttypes>
-namespace android {
-static constexpr const char* kExperimentNamespace = "surface_flinger_native_boot";
-static constexpr const int64_t kDemoFlag = -1;
+#include <com_android_graphics_surfaceflinger_flags.h>
+namespace android {
+using namespace com::android::graphics::surfaceflinger;
+
+static constexpr const char* kExperimentNamespace = "surface_flinger_native_boot";
+
+std::unique_ptr<FlagManager> FlagManager::mInstance;
+std::once_flag FlagManager::mOnce;
+
+FlagManager::FlagManager(ConstructorTag) {}
FlagManager::~FlagManager() = default;
-void FlagManager::dump(std::string& result) const {
- base::StringAppendF(&result, "FlagManager values: \n");
- base::StringAppendF(&result, "demo_flag: %" PRId64 "\n", demo_flag());
- base::StringAppendF(&result, "use_adpf_cpu_hint: %s\n", use_adpf_cpu_hint() ? "true" : "false");
- base::StringAppendF(&result, "use_skia_tracing: %s\n", use_skia_tracing() ? "true" : "false");
-}
-
namespace {
-template <typename T>
-std::optional<T> doParse(const char* str);
-
-template <>
-[[maybe_unused]] std::optional<int32_t> doParse(const char* str) {
- int32_t ret;
- return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
-}
-
-template <>
-[[maybe_unused]] std::optional<int64_t> doParse(const char* str) {
- int64_t ret;
- return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
-}
-
-template <>
-[[maybe_unused]] std::optional<bool> doParse(const char* str) {
+std::optional<bool> parseBool(const char* str) {
base::ParseBoolResult parseResult = base::ParseBool(str);
switch (parseResult) {
case base::ParseBoolResult::kTrue:
@@ -67,44 +51,153 @@
return std::nullopt;
}
}
+
+bool getFlagValue(std::function<bool()> getter, std::optional<bool> overrideValue) {
+ if (overrideValue.has_value()) {
+ return *overrideValue;
+ }
+
+ return getter();
+}
+
} // namespace
-std::string FlagManager::getServerConfigurableFlag(const std::string& experimentFlagName) const {
- return server_configurable_flags::GetServerConfigurableFlag(kExperimentNamespace,
- experimentFlagName, "");
+const FlagManager& FlagManager::getInstance() {
+ return getMutableInstance();
}
-template int32_t FlagManager::getValue<int32_t>(const std::string&, std::optional<int32_t>,
- int32_t) const;
-template int64_t FlagManager::getValue<int64_t>(const std::string&, std::optional<int64_t>,
- int64_t) const;
-template bool FlagManager::getValue<bool>(const std::string&, std::optional<bool>, bool) const;
-template <typename T>
-T FlagManager::getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
- T defaultValue) const {
- // System property takes precedence over the experiment config server value.
- if (systemPropertyOpt.has_value()) {
- return *systemPropertyOpt;
+FlagManager& FlagManager::getMutableInstance() {
+ std::call_once(mOnce, [&] {
+ LOG_ALWAYS_FATAL_IF(mInstance, "Instance already created");
+ mInstance = std::make_unique<FlagManager>(ConstructorTag{});
+ });
+
+ return *mInstance;
+}
+
+void FlagManager::markBootCompleted() {
+ mBootCompleted = true;
+}
+
+void FlagManager::setUnitTestMode() {
+ mUnitTestMode = true;
+
+ // Also set boot completed as we don't really care about it in unit testing
+ mBootCompleted = true;
+}
+
+void FlagManager::dumpFlag(std::string& result, bool readonly, const char* name,
+ std::function<bool()> getter) const {
+ if (readonly || mBootCompleted) {
+ base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false");
+ } else {
+ base::StringAppendF(&result, "%s: in progress (still booting)\n", name);
}
- std::string str = getServerConfigurableFlag(experimentFlagName);
- return str.empty() ? defaultValue : doParse<T>(str.c_str()).value_or(defaultValue);
}
-int64_t FlagManager::demo_flag() const {
- std::optional<int64_t> sysPropVal = std::nullopt;
- return getValue("DemoFeature__demo_flag", sysPropVal, kDemoFlag);
+void FlagManager::dump(std::string& result) const {
+#define DUMP_FLAG_INTERVAL(name, readonly) \
+ dumpFlag(result, (readonly), #name, std::bind(&FlagManager::name, this))
+#define DUMP_SERVER_FLAG(name) DUMP_FLAG_INTERVAL(name, false)
+#define DUMP_READ_ONLY_FLAG(name) DUMP_FLAG_INTERVAL(name, true)
+
+ base::StringAppendF(&result, "FlagManager values: \n");
+
+ /// Legacy server flags ///
+ DUMP_SERVER_FLAG(use_adpf_cpu_hint);
+ DUMP_SERVER_FLAG(use_skia_tracing);
+
+ /// Trunk stable server flags ///
+ DUMP_SERVER_FLAG(late_boot_misc2);
+ DUMP_SERVER_FLAG(dont_skip_on_early);
+ DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
+
+ /// Trunk stable readonly flags ///
+ DUMP_READ_ONLY_FLAG(connected_display);
+ DUMP_READ_ONLY_FLAG(enable_small_area_detection);
+ DUMP_READ_ONLY_FLAG(misc1);
+ DUMP_READ_ONLY_FLAG(vrr_config);
+ DUMP_READ_ONLY_FLAG(hotplug2);
+ DUMP_READ_ONLY_FLAG(hdcp_level_hal);
+ DUMP_READ_ONLY_FLAG(multithreaded_present);
+
+#undef DUMP_READ_ONLY_FLAG
+#undef DUMP_SERVER_FLAG
+#undef DUMP_FLAG_INTERVAL
}
-bool FlagManager::use_adpf_cpu_hint() const {
- std::optional<bool> sysPropVal =
- doParse<bool>(base::GetProperty("debug.sf.enable_adpf_cpu_hint", "").c_str());
- return getValue("AdpfFeature__adpf_cpu_hint", sysPropVal, false);
+std::optional<bool> FlagManager::getBoolProperty(const char* property) const {
+ return parseBool(base::GetProperty(property, "").c_str());
}
-bool FlagManager::use_skia_tracing() const {
- std::optional<bool> sysPropVal =
- doParse<bool>(base::GetProperty(PROPERTY_SKIA_ATRACE_ENABLED, "").c_str());
- return getValue("SkiaTracingFeature__use_skia_tracing", sysPropVal, false);
+bool FlagManager::getServerConfigurableFlag(const char* experimentFlagName) const {
+ const auto value = server_configurable_flags::GetServerConfigurableFlag(kExperimentNamespace,
+ experimentFlagName, "");
+ const auto res = parseBool(value.c_str());
+ return res.has_value() && res.value();
+}
+
+#define FLAG_MANAGER_LEGACY_SERVER_FLAG(name, syspropOverride, serverFlagName) \
+ bool FlagManager::name() const { \
+ LOG_ALWAYS_FATAL_IF(!mBootCompleted, \
+ "Can't read %s before boot completed as it is server writable", \
+ __func__); \
+ const auto debugOverride = getBoolProperty(syspropOverride); \
+ if (debugOverride.has_value()) return debugOverride.value(); \
+ return getServerConfigurableFlag(serverFlagName); \
+ }
+
+#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted) \
+ bool FlagManager::name() const { \
+ if (checkForBootCompleted) { \
+ LOG_ALWAYS_FATAL_IF(!mBootCompleted, \
+ "Can't read %s before boot completed as it is server writable", \
+ __func__); \
+ } \
+ static std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
+ static bool value = getFlagValue([] { return flags::name(); }, debugOverride); \
+ if (mUnitTestMode) { \
+ /* \
+ * When testing, we don't want to rely on the cached values stored in the static \
+ * variables. \
+ */ \
+ debugOverride = getBoolProperty(syspropOverride); \
+ value = getFlagValue([] { return flags::name(); }, debugOverride); \
+ } \
+ return value; \
+ }
+
+#define FLAG_MANAGER_SERVER_FLAG(name, syspropOverride) \
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true)
+
+#define FLAG_MANAGER_READ_ONLY_FLAG(name, syspropOverride) \
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false)
+
+/// Legacy server flags ///
+FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
+FLAG_MANAGER_LEGACY_SERVER_FLAG(use_adpf_cpu_hint, "debug.sf.enable_adpf_cpu_hint",
+ "AdpfFeature__adpf_cpu_hint")
+FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED,
+ "SkiaTracingFeature__use_skia_tracing")
+
+/// Trunk stable readonly flags ///
+FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
+FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
+FLAG_MANAGER_READ_ONLY_FLAG(misc1, "")
+FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config")
+FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
+FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "")
+FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
+
+/// Trunk stable server flags ///
+FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
+FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
+
+/// Exceptions ///
+bool FlagManager::dont_skip_on_early() const {
+ // Even though this is a server writable flag, we do call it before boot completed, but that's
+ // fine since the decision is done per frame. We can't do caching though.
+ return flags::dont_skip_on_early();
}
} // namespace android
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
index e834142..e3e4f80 100644
--- a/services/surfaceflinger/FlagManager.h
+++ b/services/surfaceflinger/FlagManager.h
@@ -17,32 +17,66 @@
#pragma once
#include <cstdint>
+#include <mutex>
#include <optional>
#include <string>
namespace android {
// Manages flags for SurfaceFlinger, including default values, system properties, and Mendel
-// experiment configuration values.
+// experiment configuration values. Can be called from any thread.
class FlagManager {
+private:
+ // Effectively making the constructor private, while allowing std::make_unique to work
+ struct ConstructorTag {};
+
public:
- FlagManager() = default;
+ static const FlagManager& getInstance();
+ static FlagManager& getMutableInstance();
+
+ FlagManager(ConstructorTag);
virtual ~FlagManager();
+
+ void markBootCompleted();
void dump(std::string& result) const;
- int64_t demo_flag() const;
+ void setUnitTestMode();
+ /// Legacy server flags ///
+ bool test_flag() const;
bool use_adpf_cpu_hint() const;
-
bool use_skia_tracing() const;
+ /// Trunk stable server flags ///
+ bool late_boot_misc2() const;
+ bool dont_skip_on_early() const;
+ bool refresh_rate_overlay_on_external_display() const;
+
+ /// Trunk stable readonly flags ///
+ bool connected_display() const;
+ bool enable_small_area_detection() const;
+ bool misc1() const;
+ bool vrr_config() const;
+ bool hotplug2() const;
+ bool hdcp_level_hal() const;
+ bool multithreaded_present() const;
+
+protected:
+ // overridden for unit tests
+ virtual std::optional<bool> getBoolProperty(const char*) const;
+ virtual bool getServerConfigurableFlag(const char*) const;
+
private:
- friend class FlagManagerTest;
+ friend class TestableFlagManager;
- // Wrapper for mocking in test.
- virtual std::string getServerConfigurableFlag(const std::string& experimentFlagName) const;
+ FlagManager(const FlagManager&) = delete;
- template <typename T>
- T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
- T defaultValue) const;
+ void dumpFlag(std::string& result, bool readonly, const char* name,
+ std::function<bool()> getter) const;
+
+ std::atomic_bool mBootCompleted = false;
+ std::atomic_bool mUnitTestMode = false;
+
+ static std::unique_ptr<FlagManager> mInstance;
+ static std::once_flag mOnce;
};
} // namespace android
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 1b1307b..dfbb55d 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -1074,6 +1074,66 @@
});
}
+void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousActualPresentTime) const {
+ nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0;
+ const constexpr float kThresh = 0.5f;
+ const constexpr float kRange = 1.5f;
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ if (previousActualPresentTime != 0 &&
+ static_cast<float>(mSurfaceFlingerActuals.presentTime - previousActualPresentTime) >=
+ static_cast<float>(mRenderRate.getPeriodNsecs()) * kRange &&
+ static_cast<float>(surfaceFrame->getPredictions().presentTime) <=
+ (static_cast<float>(mSurfaceFlingerActuals.presentTime) -
+ kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
+ static_cast<float>(surfaceFrame->getPredictions().presentTime) >=
+ (static_cast<float>(previousActualPresentTime) -
+ kThresh * static_cast<float>(mRenderRate.getPeriodNsecs()))) {
+ skippedFrameStartTime = surfaceFrame->getPredictions().endTime;
+ skippedFramePresentTime = surfaceFrame->getPredictions().presentTime;
+ break;
+ }
+ }
+
+ // add slice
+ if (skippedFrameStartTime != 0 && skippedFramePresentTime != 0) {
+ int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+
+ // Actual timeline start
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(static_cast<uint64_t>(skippedFrameStartTime + monoBootOffset));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start();
+
+ actualDisplayFrameStartEvent->set_cookie(actualTimelineCookie);
+
+ actualDisplayFrameStartEvent->set_token(0);
+ actualDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
+ actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ actualDisplayFrameStartEvent->set_gpu_composition(false);
+ actualDisplayFrameStartEvent->set_prediction_type(toProto(PredictionState::Valid));
+ actualDisplayFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
+ actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(JankType::Dropped));
+ });
+
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameEndEvent = event->set_frame_end();
+
+ actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+ }
+}
+
void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid,
nsecs_t monoBootOffset) const {
int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
@@ -1115,7 +1175,8 @@
});
}
-void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const {
+void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousActualPresentTime) const {
if (mSurfaceFrames.empty()) {
// We don't want to trace display frames without any surface frames updates as this cannot
// be janky
@@ -1138,6 +1199,8 @@
for (auto& surfaceFrame : mSurfaceFrames) {
surfaceFrame->trace(mToken, monoBootOffset);
}
+
+ addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousActualPresentTime);
}
float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
@@ -1229,7 +1292,7 @@
const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
+ displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime);
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
@@ -1245,7 +1308,7 @@
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
+ displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPresentTime);
mPreviousPresentTime = signalTime;
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 538ea12..6598e21 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -378,7 +378,8 @@
// Emits a packet for perfetto tracing. The function body will be executed only if tracing
// is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME
// and SYSTEM_TIME_MONOTONIC.
- void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
+ void trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousActualPresentTime) const;
// Sets the token, vsyncPeriod, predictions and SF start time.
void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate,
std::optional<TimelineItem> predictions, nsecs_t wakeUpTime);
@@ -411,6 +412,8 @@
void dump(std::string& result, nsecs_t baseTime) const;
void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
+ void addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousActualPresentTime) const;
void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
nsecs_t previousPresentTime);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 7a85da0..38974a2 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -332,6 +332,14 @@
return geomBufferSize.toFloatRect();
}
+bool LayerSnapshot::isFrontBuffered() const {
+ if (!externalTexture) {
+ return false;
+ }
+
+ return externalTexture->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
+}
+
Hwc2::IComposerClient::BlendMode LayerSnapshot::getBlendMode(
const RequestedLayerState& requested) const {
auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 4fd6495..73ee22f 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -143,6 +143,7 @@
std::string getIsVisibleReason() const;
bool hasInputInfo() const;
FloatRect sourceBounds() const;
+ bool isFrontBuffered() const;
Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const;
friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj);
void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 2a0857d..9476ff4 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -739,6 +739,7 @@
!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 @@
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 700baa2..3ca83e0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -136,6 +136,7 @@
using gui::GameMode;
using gui::LayerMetadata;
using gui::WindowInfo;
+using ui::Size;
using PresentState = frametimeline::SurfaceFrame::PresentState;
@@ -2591,6 +2592,9 @@
}
}
+ Rect bufferSize = getBufferSize(getDrawingState());
+ info.contentSize = Size(bufferSize.width(), bufferSize.height());
+
return info;
}
@@ -4024,10 +4028,10 @@
return getAlpha() > 0.0f || hasBlur();
}
-void Layer::onPostComposition(const DisplayDevice* display,
- const std::shared_ptr<FenceTime>& glDoneFence,
- const std::shared_ptr<FenceTime>& presentFence,
- const CompositorTiming& compositorTiming) {
+void Layer::onCompositionPresented(const DisplayDevice* display,
+ const std::shared_ptr<FenceTime>& glDoneFence,
+ const std::shared_ptr<FenceTime>& presentFence,
+ const CompositorTiming& compositorTiming) {
// mFrameLatencyNeeded is true when a new frame was latched for the
// composition.
if (!mBufferInfo.mFrameLatencyNeeded) return;
@@ -4230,6 +4234,14 @@
return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace;
}
+bool Layer::isFrontBuffered() const {
+ if (mBufferInfo.mBuffer == nullptr) {
+ return false;
+ }
+
+ return mBufferInfo.mBuffer->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
+}
+
ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) {
ui::Dataspace updatedDataspace = dataspace;
// translate legacy dataspaces to modern dataspaces
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index dd91adc..f715910 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -342,6 +342,8 @@
//
ui::Dataspace getDataSpace() const;
+ virtual bool isFrontBuffered() const;
+
virtual sp<LayerFE> getCompositionEngineLayerFE() const;
virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
@@ -433,13 +435,10 @@
void updateCloneBufferInfo();
uint64_t mPreviousFrameNumber = 0;
- /*
- * called after composition.
- * returns true if the layer latched a new buffer this frame.
- */
- void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/,
- const std::shared_ptr<FenceTime>& /*presentFence*/,
- const CompositorTiming&);
+ void onCompositionPresented(const DisplayDevice*,
+ const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+ const std::shared_ptr<FenceTime>& /*presentFence*/,
+ const CompositorTiming&);
// If a buffer was replaced this frame, release the former buffer
void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/);
@@ -915,14 +914,13 @@
void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now);
void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now);
auto getLayerProps() const {
- return scheduler::LayerProps{
- .visible = isVisible(),
- .bounds = getBounds(),
- .transform = getTransform(),
- .setFrameRateVote = getFrameRateForLayerTree(),
- .frameRateSelectionPriority = getFrameRateSelectionPriority(),
- .isSmallDirty = mSmallDirty,
- };
+ return scheduler::LayerProps{.visible = isVisible(),
+ .bounds = getBounds(),
+ .transform = getTransform(),
+ .setFrameRateVote = getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = getFrameRateSelectionPriority(),
+ .isSmallDirty = mSmallDirty,
+ .isFrontBuffered = isFrontBuffered()};
};
bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 42676c6..6752a0b 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -17,9 +17,9 @@
#include <algorithm>
#include "Client.h"
+#include "FlagManager.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
-#include "Utils/FlagUtils.h"
#include <SkSurface.h>
@@ -268,7 +268,7 @@
}
void RefreshRateOverlay::changeRenderRate(Fps renderFps) {
- if (mFeatures.test(Features::RenderRate) && mVsyncRate && flagutils::vrrConfigEnabled()) {
+ if (mFeatures.test(Features::RenderRate) && mVsyncRate && FlagManager::getInstance().misc1()) {
mRenderFps = renderFps;
const auto buffer = getOrCreateBuffers(*mVsyncRate, renderFps)[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 9a55c94..7f627f8 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -45,6 +45,7 @@
#include <scheduler/VsyncConfig.h>
#include "DisplayHardware/DisplayMode.h"
+#include "FlagManager.h"
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
@@ -308,7 +309,7 @@
auto connection = sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
IPCThreadState::self()->getCallingUid(),
eventRegistration);
- if (flags::misc1()) {
+ if (FlagManager::getInstance().misc1()) {
const int policy = SCHED_FIFO;
connection->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 069d89b..450ba1d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -33,6 +33,7 @@
#include "../Layer.h"
#include "EventThread.h"
+#include "FlagManager.h"
#include "LayerInfo.h"
namespace android::scheduler {
@@ -40,12 +41,21 @@
namespace {
bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
- // Layers with an explicit frame rate or frame rate category are always kept active,
+ if (FlagManager::getInstance().misc1() && !info.isVisible()) {
+ return false;
+ }
+
+ // Layers with an explicit frame rate or frame rate category are kept active,
// but ignore NoVote.
if (info.getSetFrameRateVote().isValid() && !info.getSetFrameRateVote().isNoVote()) {
return true;
}
+ // Make all front buffered layers active
+ if (FlagManager::getInstance().vrr_config() && info.isFrontBuffered() && info.isVisible()) {
+ return true;
+ }
+
return info.isVisible() && info.getLastUpdatedTime() >= threshold;
}
@@ -157,6 +167,27 @@
info->setDefaultLayerVote(getVoteType(frameRateCompatibility, contentDetectionEnabled));
}
+void LayerHistory::setLayerProperties(int32_t id, const LayerProps& properties) {
+ std::lock_guard lock(mLock);
+
+ auto [found, layerPair] = findLayer(id);
+ if (found == LayerStatus::NotFound) {
+ // Offscreen layer
+ ALOGV("%s: %d not registered", __func__, id);
+ return;
+ }
+
+ const auto& info = layerPair->second;
+ info->setProperties(properties);
+
+ // Activate layer if inactive and visible.
+ if (found == LayerStatus::LayerInInactiveMap && info->isVisible()) {
+ mActiveLayerInfos.insert(
+ {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
+ mInactiveLayerInfos.erase(id);
+ }
+}
+
auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
ATRACE_CALL();
Summary summary;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index bac1ec6..5a9445b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -73,7 +73,7 @@
// does not set a preference for refresh rate.
void setDefaultFrameRateCompatibility(int32_t id, FrameRateCompatibility frameRateCompatibility,
bool contentDetectionEnabled);
-
+ void setLayerProperties(int32_t id, const LayerProps&);
using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 36f2475..bf3a7bc 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -75,6 +75,10 @@
}
}
+void LayerInfo::setProperties(const android::scheduler::LayerProps& properties) {
+ *mLayerProps = properties;
+}
+
bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
mFrameTimeValidSince.time_since_epoch())
@@ -304,19 +308,22 @@
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
if (mLayerVote.category != FrameRateCategory::Default) {
- ATRACE_FORMAT_INSTANT("ExplicitCategory (%s)",
+ const auto voteType = mLayerVote.type == LayerHistory::LayerVoteType::NoVote
+ ? LayerHistory::LayerVoteType::NoVote
+ : LayerHistory::LayerVoteType::ExplicitCategory;
+ ATRACE_FORMAT_INSTANT("Vote %s (category=%s)", ftl::enum_string(voteType).c_str(),
ftl::enum_string(mLayerVote.category).c_str());
- ALOGV("%s uses frame rate category: %d", mName.c_str(),
- static_cast<int>(mLayerVote.category));
- votes.push_back({LayerHistory::LayerVoteType::ExplicitCategory, Fps(),
- Seamlessness::Default, mLayerVote.category,
+ ALOGV("%s voted %s with category: %s", mName.c_str(),
+ ftl::enum_string(voteType).c_str(),
+ ftl::enum_string(mLayerVote.category).c_str());
+ votes.push_back({voteType, Fps(), Seamlessness::Default, mLayerVote.category,
mLayerVote.categorySmoothSwitchOnly});
}
if (mLayerVote.fps.isValid() ||
mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) {
ATRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str());
- ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
+ ALOGV("%s voted %d", mName.c_str(), static_cast<int>(mLayerVote.type));
votes.push_back(mLayerVote);
}
@@ -331,6 +338,14 @@
return votes;
}
+ // Vote for max refresh rate whenever we're front-buffered.
+ if (FlagManager::getInstance().vrr_config() && isFrontBuffered()) {
+ ATRACE_FORMAT_INSTANT("front buffered");
+ ALOGV("%s is front-buffered", mName.c_str());
+ votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
+ return votes;
+ }
+
const LayerInfo::Frequent frequent = isFrequent(now);
mIsFrequencyConclusive = frequent.isConclusive;
if (!frequent.isFrequent) {
@@ -391,6 +406,10 @@
return mLayerProps->frameRateSelectionPriority;
}
+bool LayerInfo::isFrontBuffered() const {
+ return mLayerProps->isFrontBuffered;
+}
+
FloatRect LayerInfo::getBounds() const {
return mLayerProps->bounds;
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 7d3cffa..d24fc33 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -182,6 +182,8 @@
// layer can go back to whatever vote it had before the app voted for it.
void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
+ void setProperties(const LayerProps&);
+
// Resets the layer vote to its default.
void resetLayerVote() {
mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default, FrameRateCategory::Default};
@@ -200,6 +202,7 @@
FrameRate getSetFrameRateVote() const;
bool isVisible() const;
int32_t getFrameRateSelectionPriority() const;
+ bool isFrontBuffered() const;
FloatRect getBounds() const;
ui::Transform getTransform() const;
@@ -360,6 +363,7 @@
LayerInfo::FrameRate setFrameRateVote;
int32_t frameRateSelectionPriority = -1;
bool isSmallDirty = false;
+ bool isFrontBuffered = false;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 1d23fb5..5892b2b 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -38,7 +38,6 @@
#include "../SurfaceFlingerProperties.h"
#include "RefreshRateSelector.h"
-#include "Utils/FlagUtils.h"
#include <com_android_graphics_surfaceflinger_flags.h>
@@ -115,7 +114,7 @@
using fps_approx_ops::operator/;
// use signed type as `fps / range.max` might be 0
auto start = std::max(1, static_cast<int>(peakFps / range.max) - 1);
- if (flagutils::vrrConfigEnabled()) {
+ if (FlagManager::getInstance().vrr_config()) {
start = std::max(1,
static_cast<int>(vsyncRate /
std::min(range.max, peakFps, fps_approx_ops::operator<)) -
@@ -401,7 +400,6 @@
float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
bool isSeamlessSwitch) const {
- ATRACE_CALL();
// Slightly prefer seamless switches.
constexpr float kSeamedSwitchPenalty = 0.95f;
const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5b36a5e..f41243c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -47,6 +47,7 @@
#include "../Layer.h"
#include "EventThread.h"
+#include "FlagManager.h"
#include "FrameRateOverrideMappings.h"
#include "FrontEnd/LayerHandle.h"
#include "OneShotTimer.h"
@@ -66,11 +67,12 @@
namespace android::scheduler {
Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features,
- sp<VsyncModulator> modulatorPtr)
+ sp<VsyncModulator> modulatorPtr, IVsyncTrackerCallback& vsyncTrackerCallback)
: impl::MessageQueue(compositor),
mFeatures(features),
mVsyncModulator(std::move(modulatorPtr)),
- mSchedulerCallback(callback) {}
+ mSchedulerCallback(callback),
+ mVsyncTrackerCallback(vsyncTrackerCallback) {}
Scheduler::~Scheduler() {
// MessageQueue depends on VsyncSchedule, so first destroy it.
@@ -115,10 +117,10 @@
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
- auto schedulePtr = std::make_shared<VsyncSchedule>(displayId, mFeatures,
- [this](PhysicalDisplayId id, bool enable) {
- onHardwareVsyncRequest(id, enable);
- });
+ auto schedulePtr = std::make_shared<VsyncSchedule>(
+ displayId, mFeatures,
+ [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); },
+ mVsyncTrackerCallback);
registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr));
}
@@ -210,7 +212,7 @@
targeters.try_emplace(id, &targeter);
}
- if (flagutils::vrrConfigEnabled() &&
+ if (FlagManager::getInstance().vrr_config() &&
CC_UNLIKELY(mPacesetterFrameDurationFractionToSkip > 0.f)) {
const auto period = pacesetterTargeter.target().expectedFrameDuration();
const auto skipDuration = Duration::fromNs(
@@ -561,7 +563,19 @@
ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
to_string(mode.modePtr->getVsyncRate()).c_str());
- display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
+ display.schedulePtr->getTracker().setDisplayModeData(
+ {.renderRate = renderFrameRate,
+ .notifyExpectedPresentTimeoutOpt = getNotifyExpectedPresentTimeout(mode)});
+}
+
+std::optional<Period> Scheduler::getNotifyExpectedPresentTimeout(const FrameRateMode& mode) {
+ if (mode.modePtr->getVrrConfig() && mode.modePtr->getVrrConfig()->notifyExpectedPresentConfig) {
+ return Period::fromNs(
+ mode.modePtr->getVrrConfig()
+ ->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs);
+ } else {
+ return std::nullopt;
+ }
}
void Scheduler::resync() {
@@ -640,6 +654,10 @@
mFeatures.test(Feature::kContentDetection));
}
+void Scheduler::setLayerProperties(int32_t id, const android::scheduler::LayerProps& properties) {
+ mLayerHistory.setLayerProperties(id, properties);
+}
+
void Scheduler::chooseRefreshRateForContent(
const surfaceflinger::frontend::LayerHierarchy* hierarchy,
bool updateAttachedChoreographer) {
@@ -1164,7 +1182,7 @@
}
}
-bool Scheduler::onPostComposition(nsecs_t presentTime) {
+bool Scheduler::onCompositionPresented(nsecs_t presentTime) {
std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
if (presentTime < mLastVsyncPeriodChangeTimeline->refreshTimeNanos) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index b0520a6..c78051a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -102,7 +102,8 @@
using Impl = android::impl::MessageQueue;
public:
- Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>);
+ Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>,
+ IVsyncTrackerCallback&);
virtual ~Scheduler();
void startTimers();
@@ -233,6 +234,7 @@
nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
void setDefaultFrameRateCompatibility(int32_t id, scheduler::FrameRateCompatibility);
+ void setLayerProperties(int32_t id, const LayerProps&);
void deregisterLayer(Layer*);
void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
@@ -281,8 +283,8 @@
// Notifies the scheduler about a refresh rate timeline change.
void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
- // Notifies the scheduler post composition. Returns if recomposite is needed.
- bool onPostComposition(nsecs_t presentTime);
+ // Notifies the scheduler once the composition is presented. Returns if recomposite is needed.
+ bool onCompositionPresented(nsecs_t presentTime);
// Notifies the scheduler when the display size has changed. Called from SF's main thread
void onActiveDisplayAreaChanged(uint32_t displayArea);
@@ -429,6 +431,9 @@
Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
void resync() override EXCLUDES(mDisplayLock);
+ std::optional<Period> getNotifyExpectedPresentTimeout(const FrameRateMode&)
+ REQUIRES(mDisplayLock);
+
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
sp<EventThreadConnection> connection;
@@ -462,6 +467,8 @@
ISchedulerCallback& mSchedulerCallback;
+ IVsyncTrackerCallback& mVsyncTrackerCallback;
+
// mDisplayLock may be locked while under mPolicyLock.
mutable std::mutex mPolicyLock;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index f467670..3e7ec49 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -25,33 +25,29 @@
#include <scheduler/TimeKeeper.h>
+#include "FlagManager.h"
#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"
-#include <com_android_graphics_surfaceflinger_flags.h>
-
#undef LOG_TAG
#define LOG_TAG "VSyncDispatch"
namespace android::scheduler {
-using namespace com::android::graphics::surfaceflinger;
using base::StringAppendF;
namespace {
-nsecs_t getExpectedCallbackTime(nsecs_t now, nsecs_t nextVsyncTime,
+nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
const VSyncDispatch::ScheduleTiming& timing) {
- const auto expectedCallbackTime = nextVsyncTime - timing.readyDuration - timing.workDuration;
- const auto baseTime = flags::dont_skip_on_early() ? now : expectedCallbackTime;
- return std::max(baseTime, expectedCallbackTime);
+ return nextVsyncTime - timing.readyDuration - timing.workDuration;
}
nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
const VSyncDispatch::ScheduleTiming& timing) {
const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
- return getExpectedCallbackTime(now, nextVsyncTime, timing);
+ return getExpectedCallbackTime(nextVsyncTime, timing);
}
} // namespace
@@ -105,23 +101,25 @@
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
bool const wouldSkipAWakeup =
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
- if (flags::dont_skip_on_early()) {
+ if (FlagManager::getInstance().dont_skip_on_early()) {
if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
- return getExpectedCallbackTime(now, mArmedInfo->mActualVsyncTime, timing);
+ nextVsyncTime = mArmedInfo->mActualVsyncTime;
+ } else {
+ nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
}
+ nextWakeupTime = std::max(now, nextVsyncTime - timing.workDuration - timing.readyDuration);
} else {
if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
- return getExpectedCallbackTime(now, nextVsyncTime, timing);
+ return getExpectedCallbackTime(nextVsyncTime, timing);
}
+ nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
+ nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
}
- nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
- nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
-
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
mScheduleTiming = timing;
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
- return getExpectedCallbackTime(now, nextVsyncTime, timing);
+ return nextWakeupTime;
}
void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index e969fdc..57aa010 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -35,6 +35,7 @@
#include <gui/TraceUtils.h>
#include <utils/Log.h>
+#include "FlagManager.h"
#include "RefreshRateSelector.h"
#include "VSyncPredictor.h"
@@ -47,12 +48,14 @@
VSyncPredictor::~VSyncPredictor() = default;
VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize,
- size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
+ size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent,
+ IVsyncTrackerCallback& callback)
: mId(id),
mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
kHistorySize(historySize),
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
+ mVsyncTrackerCallback(callback),
mIdealPeriod(idealPeriod) {
resetModel();
}
@@ -275,11 +278,11 @@
mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
- if (!mRenderRate) return 0;
+ if (!mDisplayModeDataOpt) return 0;
const auto divisor =
RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod),
- *mRenderRate);
+ mDisplayModeDataOpt->renderRate);
if (divisor <= 1) return 0;
const int mod = mLastVsyncSequence->seq % divisor;
@@ -289,12 +292,29 @@
}();
if (renderRatePhase == 0) {
- return mLastVsyncSequence->vsyncTime;
+ const auto vsyncTime = mLastVsyncSequence->vsyncTime;
+ if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) {
+ const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
+ ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__,
+ ticks<std::milli, float>(vsyncTimePoint - TimePoint::now()));
+ mVsyncTrackerCallback.onVsyncGenerated(mId, vsyncTimePoint, *mDisplayModeDataOpt,
+ Period::fromNs(mIdealPeriod));
+ }
+ return vsyncTime;
}
auto const [slope, intercept] = getVSyncPredictionModelLocked();
const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
- return nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
+ const auto nextAnticipatedVsyncTime =
+ nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
+ if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) {
+ const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime);
+ ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__,
+ ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now()));
+ mVsyncTrackerCallback.onVsyncGenerated(mId, nextAnticipatedVsyncTimePoint,
+ *mDisplayModeDataOpt, Period::fromNs(mIdealPeriod));
+ }
+ return nextAnticipatedVsyncTime;
}
/*
@@ -332,10 +352,14 @@
return vsyncSequence.seq % divisor == 0;
}
-void VSyncPredictor::setRenderRate(Fps fps) {
- ALOGV("%s %s: %s", __func__, to_string(mId).c_str(), to_string(fps).c_str());
+void VSyncPredictor::setDisplayModeData(const DisplayModeData& displayModeData) {
+ ALOGV("%s %s: RenderRate %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(),
+ to_string(displayModeData.renderRate).c_str(),
+ displayModeData.notifyExpectedPresentTimeoutOpt
+ ? std::to_string(displayModeData.notifyExpectedPresentTimeoutOpt->ns()).c_str()
+ : "N/A");
std::lock_guard lock(mMutex);
- mRenderRate = fps;
+ mDisplayModeDataOpt = displayModeData;
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
@@ -358,6 +382,7 @@
mRateMap.erase(mRateMap.begin());
}
+ // TODO(b/308610306) mIdealPeriod to be updated with setDisplayModeData
mIdealPeriod = period;
if (mRateMap.find(period) == mRateMap.end()) {
mRateMap[mIdealPeriod] = {period, 0};
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index c01c44d..c271eb7 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -36,9 +36,11 @@
* \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
* predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
* samples that fall outlierTolerancePercent from an anticipated vsync event.
+ * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker.
*/
VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize,
- size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent);
+ size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent,
+ IVsyncTrackerCallback&);
~VSyncPredictor();
bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
@@ -69,7 +71,7 @@
bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
- void setRenderRate(Fps) final EXCLUDES(mMutex);
+ void setDisplayModeData(const DisplayModeData&) final EXCLUDES(mMutex);
void dump(std::string& result) const final EXCLUDES(mMutex);
@@ -99,6 +101,7 @@
size_t const kHistorySize;
size_t const kMinimumSamplesForPrediction;
size_t const kOutlierTolerancePercent;
+ IVsyncTrackerCallback& mVsyncTrackerCallback;
std::mutex mutable mMutex;
nsecs_t mIdealPeriod GUARDED_BY(mMutex);
@@ -110,7 +113,7 @@
size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
- std::optional<Fps> mRenderRate GUARDED_BY(mMutex);
+ std::optional<DisplayModeData> mDisplayModeDataOpt GUARDED_BY(mMutex);
mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
};
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index bc0e3bc..7eedc31 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -16,6 +16,7 @@
#pragma once
+#include <ui/DisplayId.h>
#include <utils/Timers.h>
#include <scheduler/Fps.h>
@@ -23,6 +24,23 @@
#include "VSyncDispatch.h"
namespace android::scheduler {
+
+struct DisplayModeData {
+ Fps renderRate;
+ std::optional<Period> notifyExpectedPresentTimeoutOpt;
+
+ bool operator==(const DisplayModeData& other) const {
+ return isApproxEqual(renderRate, other.renderRate) &&
+ notifyExpectedPresentTimeoutOpt == other.notifyExpectedPresentTimeoutOpt;
+ }
+};
+
+struct IVsyncTrackerCallback {
+ virtual ~IVsyncTrackerCallback() = default;
+ virtual void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime,
+ const DisplayModeData&, Period vsyncPeriod) = 0;
+};
+
/*
* VSyncTracker is an interface for providing estimates on future Vsync signal times based on
* historical vsync timing data.
@@ -80,16 +98,20 @@
virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
/*
- * Sets a render rate on the tracker. If the render rate is not a divisor
- * of the period, the render rate is ignored until the period changes.
+ * Sets the metadata about the currently active display mode such as VRR
+ * timeout period, vsyncPeriod and framework property such as render rate.
+ * If the render rate is not a divisor of the period, the render rate is
+ * ignored until the period changes.
* The tracker will continue to track the vsync timeline and expect it
* to match the current period, however, nextAnticipatedVSyncTimeFrom will
* return vsyncs according to the render rate set. Setting a render rate is useful
* when a display is running at 120Hz but the render frame rate is 60Hz.
+ * When IVsyncTrackerCallback::onVsyncGenerated callback is made we will pass along
+ * the vsyncPeriod, render rate and timeoutNs.
*
- * \param [in] Fps The render rate the tracker should operate at.
+ * \param [in] DisplayModeData The DisplayModeData the tracker will use.
*/
- virtual void setRenderRate(Fps) = 0;
+ virtual void setDisplayModeData(const DisplayModeData&) = 0;
virtual void dump(std::string& result) const = 0;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index ff3f29d..5fb53f9 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -54,10 +54,11 @@
};
VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features,
- RequestHardwareVsync requestHardwareVsync)
+ RequestHardwareVsync requestHardwareVsync,
+ IVsyncTrackerCallback& callback)
: mId(id),
mRequestHardwareVsync(std::move(requestHardwareVsync)),
- mTracker(createTracker(id)),
+ mTracker(createTracker(id, callback)),
mDispatch(createDispatch(mTracker)),
mController(createController(id, *mTracker, features)),
mTracer(features.test(Feature::kTracePredictedVsync)
@@ -100,7 +101,8 @@
mDispatch->dump(out);
}
-VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) {
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id,
+ IVsyncTrackerCallback& callback) {
// TODO(b/144707443): Tune constants.
constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
constexpr size_t kHistorySize = 20;
@@ -108,7 +110,8 @@
constexpr uint32_t kDiscardOutlierPercent = 20;
return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize,
- kMinSamplesForPrediction, kDiscardOutlierPercent);
+ kMinSamplesForPrediction, kDiscardOutlierPercent,
+ callback);
}
VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) {
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 47e92e1..ca61f87 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -31,6 +31,7 @@
#include <scheduler/Time.h>
#include "ThreadContext.h"
+#include "VSyncTracker.h"
namespace android {
class EventThreadTest;
@@ -56,7 +57,7 @@
public:
using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>;
- VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync);
+ VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync, IVsyncTrackerCallback&);
~VsyncSchedule();
// IVsyncSource overrides:
@@ -124,7 +125,7 @@
friend class android::VsyncScheduleTest;
friend class android::fuzz::SchedulerFuzzer;
- static TrackerPtr createTracker(PhysicalDisplayId);
+ static TrackerPtr createTracker(PhysicalDisplayId, IVsyncTrackerCallback&);
static DispatchPtr createDispatch(TrackerPtr);
static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags);
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 19e951a..2806450 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -83,7 +83,7 @@
};
// The frame rate category of a Layer.
-enum class FrameRateCategory {
+enum class FrameRateCategory : int32_t {
Default,
NoPreference,
Low,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fc51721..437e27e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -103,6 +103,7 @@
#include <cinttypes>
#include <cmath>
#include <cstdint>
+#include <filesystem>
#include <functional>
#include <memory>
#include <mutex>
@@ -156,15 +157,12 @@
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
#include "Utils/Dumper.h"
-#include "Utils/FlagUtils.h"
#include "WindowInfosListenerInvoker.h"
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
-
#undef NO_THREAD_SAFETY_ANALYSIS
#define NO_THREAD_SAFETY_ANALYSIS \
_Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"")
@@ -174,8 +172,6 @@
#define DOES_CONTAIN_BORDER false
namespace android {
-using namespace com::android::graphics::surfaceflinger;
-
using namespace std::chrono_literals;
using namespace std::string_literals;
using namespace std::string_view_literals;
@@ -309,6 +305,19 @@
return LayerHandle::getLayerId(surfaceControl->getHandle());
}
+/**
+ * Returns true if the file at path exists and is newer than duration.
+ */
+bool fileNewerThan(const std::string& path, std::chrono::minutes duration) {
+ using Clock = std::filesystem::file_time_type::clock;
+ std::error_code error;
+ std::filesystem::file_time_type updateTime = std::filesystem::last_write_time(path, error);
+ if (error) {
+ return false;
+ }
+ return duration > (Clock::now() - updateTime);
+}
+
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -495,11 +504,6 @@
base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true);
mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
-
- // Trunk-Stable flags
- mMiscFlagValue = flags::misc1();
- mConnectedDisplayFlagValue = flags::connected_display();
- mMisc2FlagEarlyBootValue = flags::late_boot_misc2();
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -675,6 +679,7 @@
return;
}
mBootFinished = true;
+ FlagManager::getMutableInstance().markBootCompleted();
if (mStartPropertySetThread->join() != NO_ERROR) {
ALOGE("Join StartPropertySetThread failed!");
}
@@ -688,7 +693,7 @@
mFrameTracer->initialize();
mFrameTimeline->onBootFinished();
- getRenderEngine().setEnableTracing(mFlagManager.use_skia_tracing());
+ getRenderEngine().setEnableTracing(FlagManager::getInstance().use_skia_tracing());
// wait patiently for the window manager death
const String16 name("window");
@@ -717,7 +722,7 @@
readPersistentProperties();
mPowerAdvisor->onBootFinished();
- const bool hintSessionEnabled = mFlagManager.use_adpf_cpu_hint();
+ const bool hintSessionEnabled = FlagManager::getInstance().use_adpf_cpu_hint();
mPowerAdvisor->enablePowerHintSession(hintSessionEnabled);
const bool hintSessionUsed = mPowerAdvisor->usePowerHintSession();
ALOGD("Power hint is %s",
@@ -741,10 +746,6 @@
enableRefreshRateOverlay(true);
}
}));
-
- LOG_ALWAYS_FATAL_IF(flags::misc1() != mMiscFlagValue, "misc1 flag is not boot stable!");
-
- mMisc2FlagLateBootValue = flags::late_boot_misc2();
}
static std::optional<renderengine::RenderEngine::RenderEngineType>
@@ -899,7 +900,7 @@
TransactionTraceWriter::getInstance().setWriterFunction(
[&](const std::string& filename, bool overwrite) {
auto writeFn = [&]() {
- if (!overwrite && access(filename.c_str(), F_OK) == 0) {
+ if (!overwrite && fileNewerThan(filename, std::chrono::minutes{10})) {
ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str());
return;
}
@@ -2087,7 +2088,7 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- if (mConnectedDisplayFlagValue) {
+ if (FlagManager::getInstance().connected_display()) {
// use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32
if (mIsHotplugErrViaNegVsync && timestamp < 0 && vsyncPeriod.has_value() &&
vsyncPeriod.value() == ~0) {
@@ -2218,7 +2219,8 @@
snapshot->changes.any(Changes::Geometry));
const bool hasChanges =
- snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) ||
+ snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation |
+ Changes::Geometry | Changes::Visibility) ||
(snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) !=
0;
@@ -2246,8 +2248,13 @@
.setFrameRateVote = snapshot->frameRate,
.frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
.isSmallDirty = snapshot->isSmallDirty,
+ .isFrontBuffered = snapshot->isFrontBuffered(),
};
+ if (snapshot->changes.any(Changes::Geometry | Changes::Visibility)) {
+ mScheduler->setLayerProperties(snapshot->sequence, layerProps);
+ }
+
if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
snapshot->defaultFrameRateCompatibility);
@@ -2619,6 +2626,10 @@
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
+ if (display->getId() == pacesetterId) {
+ // TODO(b/255601557) Update frameInterval per display
+ refreshArgs.frameInterval = display->refreshRateSelector().getActiveMode().fps;
+ }
}
mPowerAdvisor->setDisplays(displayIds);
@@ -2746,11 +2757,11 @@
mPowerAdvisor->reportActualWorkDuration();
}
- if (mScheduler->onPostComposition(presentTime)) {
+ if (mScheduler->onCompositionPresented(presentTime)) {
scheduleComposite(FrameHint::kNone);
}
- postComposition(pacesetterId, frameTargeters, presentTime);
+ onCompositionPresented(pacesetterId, frameTargeters, presentTime);
const bool hadGpuComposited =
multiDisplayUnion(mCompositionCoverage).test(CompositionCoverage::Gpu);
@@ -2907,11 +2918,10 @@
return ui::ROTATION_0;
}
-void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId,
- const scheduler::FrameTargeters& frameTargeters,
- nsecs_t presentStartTime) {
+void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters& frameTargeters,
+ nsecs_t presentStartTime) {
ATRACE_CALL();
- ALOGV(__func__);
ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences;
ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences;
@@ -3003,8 +3013,9 @@
mLayersWithBuffersRemoved.clear();
for (const auto& layer: mLayersWithQueuedFrames) {
- layer->onPostComposition(pacesetterDisplay.get(), pacesetterGpuCompositionDoneFenceTime,
- pacesetterPresentFenceTime, compositorTiming);
+ layer->onCompositionPresented(pacesetterDisplay.get(),
+ pacesetterGpuCompositionDoneFenceTime,
+ pacesetterPresentFenceTime, compositorTiming);
layer->releasePendingBuffer(presentTime.ns());
}
@@ -3798,7 +3809,6 @@
// 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());
@@ -4035,6 +4045,21 @@
}
}
+void SurfaceFlinger::onVsyncGenerated(PhysicalDisplayId displayId, TimePoint expectedPresentTime,
+ const scheduler::DisplayModeData& displayModeData,
+ Period vsyncPeriod) {
+ const auto status =
+ getHwComposer()
+ .notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime,
+ displayModeData.renderRate,
+ displayModeData
+ .notifyExpectedPresentTimeoutOpt);
+ if (status != NO_ERROR) {
+ ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__,
+ displayId.value);
+ }
+}
+
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
using namespace scheduler;
@@ -4051,7 +4076,7 @@
if (sysprop::use_content_detection_for_refresh_rate(false)) {
features |= Feature::kContentDetection;
- if (flags::enable_small_area_detection()) {
+ if (FlagManager::getInstance().enable_small_area_detection()) {
features |= Feature::kSmallDirtyContentDetection;
}
}
@@ -4073,8 +4098,12 @@
mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
static_cast<ISchedulerCallback&>(*this), features,
- std::move(modulatorPtr));
+ std::move(modulatorPtr),
+ static_cast<IVsyncTrackerCallback&>(*this));
mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
+ if (FlagManager::getInstance().vrr_config()) {
+ mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps);
+ }
mScheduler->startTimers();
const auto configs = mVsyncConfiguration->getCurrentConfigs();
@@ -4879,6 +4908,7 @@
.transform = layer->getTransform(),
.setFrameRateVote = layer->getFrameRateForLayerTree(),
.frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+ .isFrontBuffered = layer->isFrontBuffered(),
};
layer->recordLayerHistoryAnimationTx(layerProps, now);
}
@@ -5964,7 +5994,9 @@
});
out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy() << "\n\n";
+ << mLayerHierarchyBuilder.getHierarchy()
+ << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n";
compositionLayers = out.str();
dumpHwcLayersMinidump(compositionLayers);
}
@@ -6244,7 +6276,9 @@
});
out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy().dump() << "\n\n";
+ << mLayerHierarchyBuilder.getHierarchy().dump()
+ << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy().dump() << "\n\n";
result.append(out.str());
})
.get();
@@ -6461,17 +6495,6 @@
result.append("SurfaceFlinger global state:\n");
colorizer.reset(result);
- StringAppendF(&result, "MiscFlagValue: %s\n", mMiscFlagValue ? "true" : "false");
- StringAppendF(&result, "ConnectedDisplayFlagValue: %s\n",
- mConnectedDisplayFlagValue ? "true" : "false");
- StringAppendF(&result, "Misc2FlagValue: %s (%s after boot)\n",
- mMisc2FlagLateBootValue ? "true" : "false",
- mMisc2FlagEarlyBootValue == mMisc2FlagLateBootValue ? "stable" : "modified");
- StringAppendF(&result, "VrrConfigFlagValue: %s\n",
- flagutils::vrrConfigEnabled() ? "true" : "false");
- StringAppendF(&result, "DontSkipOnEarlyFlagValue: %s\n",
- flags::dont_skip_on_early() ? "true" : "false");
-
getRenderEngine().dump(result);
result.append("ClientCache state:\n");
@@ -6548,7 +6571,7 @@
/*
* Dump flag/property manager state
*/
- mFlagManager.dump(result);
+ FlagManager::getInstance().dump(result);
result.append(mTimeStats->miniDump());
result.append("\n");
@@ -7213,7 +7236,7 @@
// Second argument is a delay in ms for triggering the jank. This is useful for working
// with tools that steal the adb connection. This argument is optional.
case 1045: {
- if (flagutils::vrrConfigEnabled()) {
+ if (FlagManager::getInstance().vrr_config()) {
float jankAmount = data.readFloat();
int32_t jankDelayMs = 0;
if (data.readInt32(&jankDelayMs) != NO_ERROR) {
@@ -8397,7 +8420,8 @@
void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
for (const auto& [id, display] : mPhysicalDisplays) {
- if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+ if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal ||
+ FlagManager::getInstance().refresh_rate_overlay_on_external_display()) {
if (const auto device = getDisplayDeviceLocked(id)) {
const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD(
kMainThreadContext) {
@@ -9064,7 +9088,7 @@
const sp<Client> client = sp<Client>::make(mFlinger);
if (client->initCheck() == NO_ERROR) {
*outClient = client;
- if (flags::misc1()) {
+ if (FlagManager::getInstance().misc1()) {
const int policy = SCHED_FIFO;
client->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 96b67b8..1e90340 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -195,7 +195,8 @@
private HWC2::ComposerCallback,
private ICompositor,
private scheduler::ISchedulerCallback,
- private compositionengine::ICEPowerCallback {
+ private compositionengine::ICEPowerCallback,
+ private scheduler::IVsyncTrackerCallback {
public:
struct SkipInitializationTag {};
@@ -656,6 +657,10 @@
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
+ // IVsyncTrackerCallback overrides
+ void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime,
+ const scheduler::DisplayModeData&, Period vsyncPeriod) override;
+
// Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
void toggleKernelIdleTimer() REQUIRES(mStateLock);
@@ -982,8 +987,8 @@
/*
* Compositing
*/
- void postComposition(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&,
- nsecs_t presentStartTime) REQUIRES(kMainThreadContext);
+ void onCompositionPresented(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&,
+ nsecs_t presentStartTime) REQUIRES(kMainThreadContext);
/*
* Display management
@@ -1421,8 +1426,6 @@
const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
- FlagManager mFlagManager;
-
// returns the framerate of the layer with the given sequence ID
float getLayerFramerate(nsecs_t now, int32_t id) const {
return mScheduler->getLayerFramerate(now, id);
@@ -1459,12 +1462,6 @@
void sfdo_setDebugFlash(int delay);
void sfdo_scheduleComposite();
void sfdo_scheduleCommit();
-
- // Trunk-Stable flags
- bool mMiscFlagValue;
- bool mConnectedDisplayFlagValue;
- bool mMisc2FlagEarlyBootValue;
- bool mMisc2FlagLateBootValue;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index f339d22..3b2bbb0 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -31,6 +31,9 @@
},
{
"name": "CtsSurfaceControlTests"
+ },
+ {
+ "name": "CtsSurfaceControlTestsStaging"
}
],
"hwasan-presubmit": [
@@ -40,10 +43,5 @@
{
"name": "libsurfaceflinger_unittest"
}
- ],
- "postsubmit": [
- {
- "name": "CtsSurfaceControlTestsStaging"
- }
]
}
diff --git a/services/surfaceflinger/Tracing/LayerDataSource.cpp b/services/surfaceflinger/Tracing/LayerDataSource.cpp
index 25e768e..ed1e2ec 100644
--- a/services/surfaceflinger/Tracing/LayerDataSource.cpp
+++ b/services/surfaceflinger/Tracing/LayerDataSource.cpp
@@ -51,8 +51,9 @@
if (config.has_mode() && config.mode() != LayerTracing::Mode::MODE_UNSPECIFIED) {
mMode = static_cast<LayerTracing::Mode>(config.mode());
} else {
- mMode = LayerTracing::Mode::MODE_GENERATED;
- ALOGD("Received config with unspecified 'mode'. Using 'GENERATED' as default");
+ mMode = LayerTracing::Mode::MODE_GENERATED_BUGREPORT_ONLY;
+ ALOGD("Received config with unspecified 'mode'."
+ " Using 'MODE_GENERATED_BUGREPORT_ONLY' as default");
}
mFlags = 0;
@@ -68,10 +69,16 @@
}
}
-void LayerDataSource::OnFlush(const LayerDataSource::FlushArgs&) {
- ALOGD("Received OnFlush event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
+void LayerDataSource::OnFlush(const LayerDataSource::FlushArgs& args) {
+ ALOGD("Received OnFlush event"
+ " (mode = 0x%02x, flags = 0x%02x, reason = 0x%" PRIx64 ", clone_target = 0x%0" PRIx64 ")",
+ mMode, mFlags, args.flush_flags.reason(), args.flush_flags.clone_target());
+
+ bool isBugreport = args.flush_flags.reason() == perfetto::FlushFlags::Reason::kTraceClone &&
+ args.flush_flags.clone_target() == perfetto::FlushFlags::CloneTarget::kBugreport;
+
if (auto* p = mLayerTracing.load()) {
- p->onFlush(mMode, mFlags);
+ p->onFlush(mMode, mFlags, isBugreport);
}
}
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 403e105..41bcdf0 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -67,9 +67,27 @@
break;
}
case Mode::MODE_GENERATED: {
+ // This tracing mode processes the buffer of transactions (owned by TransactionTracing),
+ // generates layers snapshots and writes them to perfetto. This happens every time an
+ // OnFlush event is received.
ALOGD("Started generated tracing (waiting for OnFlush event to generated layers)");
break;
}
+ case Mode::MODE_GENERATED_BUGREPORT_ONLY: {
+ // Same as MODE_GENERATED, but only when the received OnFlush event is due to a
+ // bugreport being taken. This mode exists because the generated layers trace is very
+ // large (hundreds of MB), hence we want to include it only in bugreports and not in
+ // field uploads.
+ //
+ // Note that perfetto communicates only whether the OnFlush event is due to a bugreport
+ // or not, hence we need an additional "bugreport only" tracing mode.
+ // If perfetto had communicated when the OnFlush is due to a field upload, then we could
+ // have had a single "generated" tracing mode that would have been a noop in case of
+ // field uploads.
+ ALOGD("Started 'generated bugreport only' tracing"
+ " (waiting for bugreport's OnFlush event to generate layers)");
+ break;
+ }
case Mode::MODE_DUMP: {
auto snapshot = mTakeLayersSnapshotProto(flags);
addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP);
@@ -82,10 +100,18 @@
}
}
-void LayerTracing::onFlush(Mode mode, uint32_t flags) {
+void LayerTracing::onFlush(Mode mode, uint32_t flags, bool isBugreport) {
// In "generated" mode process the buffer of transactions (owned by TransactionTracing),
- // generate a sequence of layers snapshots and write them to perfetto.
- if (mode != Mode::MODE_GENERATED) {
+ // generate layers snapshots and write them to perfetto.
+ if (mode != Mode::MODE_GENERATED && mode != Mode::MODE_GENERATED_BUGREPORT_ONLY) {
+ ALOGD("Skipping layers trace generation (not a 'generated' tracing session)");
+ return;
+ }
+
+ // In "generated bugreport only" mode skip the layers snapshot generation
+ // if the perfetto's OnFlush event is not due to a bugreport being taken.
+ if (mode == Mode::MODE_GENERATED_BUGREPORT_ONLY && !isBugreport) {
+ ALOGD("Skipping layers trace generation (not a bugreport OnFlush event)");
return;
}
@@ -147,14 +173,23 @@
}
void LayerTracing::writeSnapshotToPerfetto(const perfetto::protos::LayersSnapshotProto& snapshot,
- Mode mode) {
+ Mode srcMode) {
const auto snapshotBytes = snapshot.SerializeAsString();
LayerDataSource::Trace([&](LayerDataSource::TraceContext context) {
- if (mode != context.GetCustomTlsState()->mMode) {
+ auto dstMode = context.GetCustomTlsState()->mMode;
+ if (srcMode == Mode::MODE_GENERATED) {
+ // Layers snapshots produced by LayerTraceGenerator have srcMode == MODE_GENERATED
+ // and should be written to tracing sessions with MODE_GENERATED
+ // or MODE_GENERATED_BUGREPORT_ONLY.
+ if (dstMode != Mode::MODE_GENERATED && dstMode != Mode::MODE_GENERATED_BUGREPORT_ONLY) {
+ return;
+ }
+ } else if (srcMode != dstMode) {
return;
}
- if (!checkAndUpdateLastVsyncIdWrittenToPerfetto(mode, snapshot.vsync_id())) {
+
+ if (!checkAndUpdateLastVsyncIdWrittenToPerfetto(srcMode, snapshot.vsync_id())) {
return;
}
{
@@ -176,7 +211,7 @@
// In some situations (e.g. two bugreports taken shortly one after the other) the generated
// sequence of layers snapshots might overlap. Here we check the snapshot's vsyncid to make
// sure that in generated tracing mode a given snapshot is written only once to perfetto.
- if (mode != Mode::MODE_GENERATED) {
+ if (mode != Mode::MODE_GENERATED && mode != Mode::MODE_GENERATED_BUGREPORT_ONLY) {
return true;
}
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index fe7f06d..2895ba7 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -55,7 +55,9 @@
* and written to perfetto.
*
*
- * E.g. start active mode tracing:
+ * E.g. start active mode tracing
+ * (replace mode value with MODE_DUMP, MODE_GENERATED or MODE_GENERATED_BUGREPORT_ONLY to enable
+ * different tracing modes):
*
adb shell -t perfetto \
-c - --txt \
@@ -79,7 +81,7 @@
}
}
}
- EOF
+EOF
*
*/
class LayerTracing {
@@ -106,7 +108,7 @@
// Start event from perfetto data source
void onStart(Mode mode, uint32_t flags);
// Flush event from perfetto data source
- void onFlush(Mode mode, uint32_t flags);
+ void onFlush(Mode mode, uint32_t flags, bool isBugreport);
// Stop event from perfetto data source
void onStop(Mode mode);
diff --git a/services/surfaceflinger/Utils/FlagUtils.h b/services/surfaceflinger/Utils/FlagUtils.h
deleted file mode 100644
index 8435f04..0000000
--- a/services/surfaceflinger/Utils/FlagUtils.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * 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 <com_android_graphics_surfaceflinger_flags.h>
-#include <string>
-
-namespace android::flagutils {
-
-using namespace std::literals::string_literals;
-using namespace com::android::graphics::surfaceflinger;
-
-inline bool vrrConfigEnabled() {
- static const bool enable_vrr_config =
- base::GetBoolProperty("debug.sf.enable_vrr_config"s, false);
- return flags::vrr_config() || enable_vrr_config;
-}
-} // namespace android::flagutils
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index 0f9060d..243b8e0 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -31,6 +31,7 @@
],
static_libs: [
"android.hardware.graphics.composer@2.1-resources",
+ "libc++fs",
"libgmock",
"libgui_mocks",
"libgmock_ndk",
@@ -73,9 +74,17 @@
],
fuzz_config: {
cc: [
- "android-media-fuzzing-reports@google.com",
+ "android-cogs-eng@google.com",
],
- componentid: 155276,
+ componentid: 1075131,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libsurfaceflinger library",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index f22315a..afb5f5c 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -163,16 +163,22 @@
void DisplayHardwareFuzzer::validateDisplay(Hwc2::AidlComposer* composer, Display display) {
uint32_t outNumTypes, outNumRequests;
- composer->validateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), &outNumTypes,
- &outNumRequests);
+ const auto frameIntervalRange =
+ mFdp.ConsumeIntegralInRange<int32_t>(Fps::fromValue(1).getPeriodNsecs(),
+ Fps::fromValue(120).getPeriodNsecs());
+ composer->validateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), frameIntervalRange,
+ &outNumTypes, &outNumRequests);
}
void DisplayHardwareFuzzer::presentOrValidateDisplay(Hwc2::AidlComposer* composer,
Display display) {
int32_t outPresentFence;
uint32_t outNumTypes, outNumRequests, state;
- composer->presentOrValidateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), &outNumTypes,
- &outNumRequests, &outPresentFence, &state);
+ const auto frameIntervalRange =
+ mFdp.ConsumeIntegralInRange<int32_t>(Fps::fromValue(1).getPeriodNsecs(),
+ Fps::fromValue(120).getPeriodNsecs());
+ composer->presentOrValidateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), frameIntervalRange,
+ &outNumTypes, &outNumRequests, &outPresentFence, &state);
}
void DisplayHardwareFuzzer::setOutputBuffer(Hwc2::AidlComposer* composer, Display display) {
@@ -223,7 +229,10 @@
mHwc.getDeviceCompositionChanges(halDisplayID,
mFdp.ConsumeBool() /*frameUsesClientComposition*/,
std::chrono::steady_clock::now(),
- mFdp.ConsumeIntegral<nsecs_t>(), &outChanges);
+ mFdp.ConsumeIntegral<nsecs_t>(),
+ Fps::fromValue(
+ mFdp.ConsumeFloatingPointInRange<float>(1.f, 120.f)),
+ &outChanges);
}
void DisplayHardwareFuzzer::getDisplayedContentSamplingAttributes(HalDisplayId halDisplayID) {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
index 8978971..ce8d47e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
@@ -30,12 +30,6 @@
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 @@
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 @@
std::vector<int32_t> layerIds =
generateLayerIds(mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds));
+ std::unique_ptr<perfetto::TracingSession> tracingSession;
while (mFdp.remaining_bytes()) {
auto invokeFrametracerAPI = mFdp.PickValueInArray<const std::function<void()>>({
[&]() { mFrameTracer->registerDataSource(); },
[&]() {
- auto tracingSession = getTracingSessionForTest();
+ if (tracingSession) {
+ tracingSession->StopBlocking();
+ }
+ tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
},
[&]() { traceTimestamp(layerIds, layerIds.size()); },
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index c4077df..9b2d453 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -225,16 +225,19 @@
class TestableScheduler : public Scheduler, private ICompositor {
public:
TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
- sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
+ sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
: TestableScheduler(std::make_unique<android::mock::VsyncController>(),
std::make_shared<android::mock::VSyncTracker>(), selectorPtr,
- std::move(modulatorPtr), callback) {}
+ std::move(modulatorPtr), callback, vsyncTrackerCallback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
VsyncSchedule::TrackerPtr tracker,
std::shared_ptr<RefreshRateSelector> selectorPtr,
- sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
- : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
+ sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
+ : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr),
+ vsyncTrackerCallback) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplayInternal(displayId, std::move(selectorPtr),
std::shared_ptr<VsyncSchedule>(
@@ -400,7 +403,8 @@
} // namespace surfaceflinger::test
// TODO(b/189053744) : Create a common test/mock library for surfaceflinger
-class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback {
+class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback,
+ private scheduler::IVsyncTrackerCallback {
public:
using HotplugEvent = SurfaceFlinger::HotplugEvent;
@@ -610,8 +614,8 @@
mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool());
- mFlinger->postComposition(displayId, ftl::init::map(displayId, &frameTargeter),
- mFdp.ConsumeIntegral<nsecs_t>());
+ mFlinger->onCompositionPresented(displayId, ftl::init::map(displayId, &frameTargeter),
+ mFdp.ConsumeIntegral<nsecs_t>());
}
mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -656,6 +660,7 @@
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
scheduler::ISchedulerCallback* callback = nullptr,
+ scheduler::IVsyncTrackerCallback* vsyncTrackerCallback = nullptr,
bool hasMultipleModes = false) {
constexpr DisplayModeId kModeId60{0};
DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
@@ -678,7 +683,8 @@
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker), mRefreshRateSelector,
- std::move(modulatorPtr), *(callback ?: this));
+ std::move(modulatorPtr), *(callback ?: this),
+ *(vsyncTrackerCallback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -799,6 +805,10 @@
void triggerOnFrameRateOverridesChanged() override {}
void onChoreographerAttached() override {}
+ // IVsyncTrackerCallback overrides
+ void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&,
+ Period) override {}
+
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger =
sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 39a7ee5..7aae3c4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -133,7 +133,7 @@
ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>()));
layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
- layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming);
+ layer->onCompositionPresented(nullptr, fenceTime, fenceTime, compositorTiming);
layer->setTransform(mFdp.ConsumeIntegral<uint32_t>());
layer->setTransformToDisplayInverse(mFdp.ConsumeBool());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index a8727f9..8fcfd81 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -178,14 +178,23 @@
dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp);
}
+struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback {
+ void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&,
+ Period) override {}
+};
+
void SchedulerFuzzer::fuzzVSyncPredictor() {
uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX);
- scheduler::VSyncPredictor tracker{kDisplayId, idealPeriod, historySize,
+ VsyncTrackerCallback callback;
+ scheduler::VSyncPredictor tracker{kDisplayId,
+ idealPeriod,
+ historySize,
minimumSamplesForPrediction,
- mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
+ mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/,
+ callback};
uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
tracker.setPeriod(period);
for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 8061a8f..728708f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -100,7 +100,7 @@
return true;
}
- void setRenderRate(Fps) override {}
+ void setDisplayModeData(const scheduler::DisplayModeData&) override {}
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 9599452..9889cb9 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -34,6 +34,7 @@
#include <errno.h>
#include <hidl/LegacySupport.h>
#include <processgroup/sched_policy.h>
+#include "FlagManager.h"
#include "SurfaceFlinger.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceFlingerProperties.h"
@@ -149,7 +150,7 @@
// publish gui::ISurfaceComposer, the new AIDL interface
sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
- if (flags::misc1()) {
+ if (FlagManager::getInstance().misc1()) {
composerAIDL->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
}
sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 19d194f..bb3c94a 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -52,4 +52,27 @@
description: "Feature flag for SmallAreaDetection"
bug: "283055450"
is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+ name: "hotplug2"
+ namespace: "core_graphics"
+ description: "Feature flag for using hotplug2 HAL API"
+ bug: "303460805"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "hdcp_level_hal"
+ namespace: "core_graphics"
+ description: "Feature flag for adding a HAL API to commuicate hdcp levels"
+ bug: "285359126"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "refresh_rate_overlay_on_external_display"
+ namespace: "core_graphics"
+ description: "enable refresh rate indicator on the external display"
+ bug: "301647974"
+}
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 9269e7c..c9af432 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -47,7 +47,6 @@
ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- mCaptureArgs.displayToken = mDisplay;
}
virtual void TearDown() {
@@ -279,8 +278,6 @@
const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
sp<SurfaceControl> mBlackBgSurface;
-
- DisplayCaptureArgs mCaptureArgs;
ScreenCaptureResults mCaptureResults;
private:
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 96cc333..79864e0 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -15,6 +15,8 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <sys/types.h>
+#include <cstdint>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -31,26 +33,22 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- ASSERT_FALSE(mDisplayToken == nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode));
- const ui::Size& resolution = mode.resolution;
-
- mDisplaySize = resolution;
+ // Root surface
+ mRootSurfaceControl =
+ createLayer(String8("RootTestSurface"), mDisplayWidth, mDisplayHeight, 0);
+ ASSERT_TRUE(mRootSurfaceControl != nullptr);
+ ASSERT_TRUE(mRootSurfaceControl->isValid());
// Background surface
- mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
- resolution.getHeight(), 0);
+ mBGSurfaceControl = createLayer(String8("BG Test Surface"), mDisplayWidth, mDisplayHeight,
+ 0, mRootSurfaceControl.get());
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
// Foreground surface
- mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+ mFGSurfaceControl =
+ createLayer(String8("FG Test Surface"), 64, 64, 0, mRootSurfaceControl.get());
ASSERT_TRUE(mFGSurfaceControl != nullptr);
ASSERT_TRUE(mFGSurfaceControl->isValid());
@@ -58,7 +56,7 @@
TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
+ t.setDisplayLayerStack(mDisplay, ui::DEFAULT_LAYER_STACK);
t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
@@ -66,25 +64,22 @@
.setPosition(mFGSurfaceControl, 64, 64)
.show(mFGSurfaceControl);
});
+
+ mCaptureArgs.sourceCrop = mDisplayRect;
+ mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
virtual void TearDown() {
LayerTransactionTest::TearDown();
mBGSurfaceControl = 0;
mFGSurfaceControl = 0;
-
- // Restore display rotation
- asTransaction([&](Transaction& t) {
- Rect displayBounds{mDisplaySize};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, displayBounds, displayBounds);
- });
}
+ sp<SurfaceControl> mRootSurfaceControl;
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
std::unique_ptr<ScreenCapture> mCapture;
- sp<IBinder> mDisplayToken;
- ui::Size mDisplaySize;
+ LayerCaptureArgs mCaptureArgs;
};
TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
@@ -92,7 +87,8 @@
ASSERT_NO_FATAL_FAILURE(
layer = createLayer("test", 32, 32,
ISurfaceComposerClient::eSecure |
- ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
@@ -100,14 +96,14 @@
{
// Ensure the UID is not root because root has all permissions
UIDFaker f(AID_APP_START);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
}
UIDFaker f(AID_SYSTEM);
// By default the system can capture screenshots with secure layers but they
// will be blacked out
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
{
SCOPED_TRACE("as system");
@@ -117,10 +113,8 @@
// Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
// to receive them...we are expected to take care with the results.
- DisplayCaptureArgs args;
- args.displayToken = mDisplay;
- args.captureSecureLayers = true;
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ mCaptureArgs.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
@@ -131,7 +125,8 @@
ASSERT_NO_FATAL_FAILURE(
parentLayer = createLayer("parent-test", 32, 32,
ISurfaceComposerClient::eSecure |
- ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parentLayer, Color::RED, 32, 32));
sp<SurfaceControl> childLayer;
@@ -152,10 +147,8 @@
// Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
// to receive them...we are expected to take care with the results.
- DisplayCaptureArgs args;
- args.displayToken = mDisplay;
- args.captureSecureLayers = true;
- ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ mCaptureArgs.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
sc.expectColor(Rect(0, 0, 10, 10), Color::BLUE);
@@ -232,7 +225,7 @@
TEST_F(ScreenCaptureTest, CaptureLayerExcludeThroughDisplayArgs) {
mCaptureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
- ScreenCapture::captureDisplay(&mCapture, mCaptureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectBGColor(0, 0);
// Doesn't capture FG layer which is at 64, 64
mCapture->expectBGColor(64, 64);
@@ -605,60 +598,55 @@
mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
}
-TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) {
- uid_t fakeUid = 12345;
+TEST_F(ScreenCaptureTest, ScreenshotProtectedBuffer) {
+ const uint32_t bufferWidth = 60;
+ const uint32_t bufferHeight = 60;
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
+ sp<SurfaceControl> layer =
+ createLayer(String8("Colored surface"), bufferWidth, bufferHeight,
+ ISurfaceComposerClient::eFXSurfaceBufferState, mRootSurfaceControl.get());
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
- ISurfaceComposerClient::eFXSurfaceBufferQueue,
- mBGSurfaceControl.get()));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
- Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+ sp<Surface> surface = layer->getSurface();
+ ASSERT_TRUE(surface != nullptr);
+ sp<ANativeWindow> anw(surface);
- // Make sure red layer with the background layer is screenshot.
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
- mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED));
- // From non system uid, can't request screenshot without a specified uid.
- UIDFaker f(fakeUid);
- ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(captureArgs, mCaptureResults));
+ int fenceFd;
+ ANativeWindowBuffer* buf = nullptr;
- // Make screenshot request with current uid set. No layers were created with the current
- // uid so screenshot will be black.
- captureArgs.uid = fakeUid;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ // End test if device does not support USAGE_PROTECTED
+ // b/309965549 This check does not exit the test when running on AVDs
+ status_t err = anw->dequeueBuffer(anw.get(), &buf, &fenceFd);
+ if (err) {
+ return;
+ }
+ anw->queueBuffer(anw.get(), buf, fenceFd);
- sp<SurfaceControl> layerWithFakeUid;
- // Create a new layer with the current uid
- ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
- createLayer("new test layer", 32, 32,
- ISurfaceComposerClient::eFXSurfaceBufferQueue,
- mBGSurfaceControl.get()));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
- Transaction()
- .show(layerWithFakeUid)
- .setLayer(layerWithFakeUid, INT32_MAX)
- .setPosition(layerWithFakeUid, 128, 128)
- .apply();
+ // USAGE_PROTECTED buffer is read as a black screen
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
- // Screenshot from the fakeUid caller with the uid requested allows the layer
- // with that uid to be screenshotted. Everything else is black
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
- mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
- mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
+ ScreenCapture sc(captureResults.buffer, captureResults.capturedHdrLayers);
+ sc.expectColor(Rect(0, 0, bufferWidth, bufferHeight), Color::BLACK);
+
+ // Reading color data will expectedly result in crash, only check usage bit
+ // b/309965549 Checking that the usage bit is protected does not work for
+ // devices that do not support usage protected.
+ mCaptureArgs.allowProtected = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
+ // ASSERT_EQ(GRALLOC_USAGE_PROTECTED, GRALLOC_USAGE_PROTECTED &
+ // captureResults.buffer->getUsage());
}
-TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) {
+TEST_F(ScreenCaptureTest, CaptureLayer) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ mRootSurfaceControl.get()));
const Color layerColor = Color::RED;
const Rect bounds = Rect(10, 10, 40, 40);
@@ -666,17 +654,13 @@
Transaction()
.show(layer)
.hide(mFGSurfaceControl)
- .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setCrop(layer, bounds)
.apply();
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
-
{
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(bounds, layerColor);
mCapture->expectBorder(bounds, {63, 63, 195, 255});
}
@@ -689,17 +673,18 @@
{
// Can't screenshot test layer since it now has flag
// eLayerSkipScreenshot
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(bounds, {63, 63, 195, 255});
mCapture->expectBorder(bounds, {63, 63, 195, 255});
}
}
-TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) {
+TEST_F(ScreenCaptureTest, CaptureLayerChild) {
sp<SurfaceControl> layer;
sp<SurfaceControl> childLayer;
- ASSERT_NO_FATAL_FAILURE(
- layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ mRootSurfaceControl.get()));
ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("test layer", 0, 0,
ISurfaceComposerClient::eFXSurfaceEffect,
layer.get()));
@@ -713,7 +698,6 @@
.show(layer)
.show(childLayer)
.hide(mFGSurfaceControl)
- .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
@@ -721,11 +705,8 @@
.setCrop(childLayer, childBounds)
.apply();
- DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
-
{
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(childBounds, childColor);
mCapture->expectBorder(childBounds, layerColor);
mCapture->expectBorder(bounds, {63, 63, 195, 255});
@@ -739,7 +720,7 @@
{
// Can't screenshot child layer since the parent has the flag
// eLayerSkipScreenshot
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectColor(childBounds, {63, 63, 195, 255});
mCapture->expectBorder(childBounds, {63, 63, 195, 255});
mCapture->expectBorder(bounds, {63, 63, 195, 255});
@@ -860,14 +841,10 @@
Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply();
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplay;
-
{
// Validate that the red layer is not on screen
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
- mCapture->expectColor(Rect(0, 0, mDisplaySize.width, mDisplaySize.height),
- {63, 63, 195, 255});
+ ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
+ mCapture->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), {63, 63, 195, 255});
}
LayerCaptureArgs captureArgs;
@@ -878,42 +855,6 @@
mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_F(ScreenCaptureTest, CaptureDisplayWith90DegRotation) {
- asTransaction([&](Transaction& t) {
- Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_90, newDisplayBounds, newDisplayBounds);
- });
-
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplayToken;
- displayCaptureArgs.width = mDisplaySize.width;
- displayCaptureArgs.height = mDisplaySize.height;
- displayCaptureArgs.useIdentityTransform = true;
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-
- mCapture->expectBGColor(0, 0);
- mCapture->expectFGColor(mDisplaySize.width - 65, 65);
-}
-
-TEST_F(ScreenCaptureTest, CaptureDisplayWith270DegRotation) {
- asTransaction([&](Transaction& t) {
- Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
- t.setDisplayProjection(mDisplayToken, ui::ROTATION_270, newDisplayBounds, newDisplayBounds);
- });
-
- DisplayCaptureArgs displayCaptureArgs;
- displayCaptureArgs.displayToken = mDisplayToken;
- displayCaptureArgs.width = mDisplaySize.width;
- displayCaptureArgs.height = mDisplaySize.height;
- displayCaptureArgs.useIdentityTransform = true;
- ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-
- std::this_thread::sleep_for(std::chrono::seconds{5});
-
- mCapture->expectBGColor(mDisplayWidth - 1, mDisplaySize.height - 1);
- mCapture->expectFGColor(65, mDisplaySize.height - 65);
-}
-
TEST_F(ScreenCaptureTest, CaptureNonHdrLayer) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index d0ab105..c5d118c 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -80,29 +80,22 @@
sp<SurfaceControl> mParent;
sp<SurfaceControl> mLayer;
std::unique_ptr<ScreenCapture> mCapture;
+ gui::LayerCaptureArgs captureArgs;
};
TEST_F(TextureFilteringTest, NoFiltering) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
- captureArgs.sourceCrop = Rect{100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
}
TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
- Transaction().setBufferCrop(mLayer, Rect{0, 0, 100, 100}).apply();
-
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
@@ -112,24 +105,20 @@
TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
// Expect filtering because the output source crop is stretched to the output buffer's size.
TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
+ captureArgs.frameScaleX = 2;
+ captureArgs.frameScaleY = 2;
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
@@ -138,20 +127,17 @@
// buffer's size.
TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
-
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 100;
- captureArgs.height = 100;
+ captureArgs.frameScaleX = 2;
+ captureArgs.frameScaleY = 2;
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mParent->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
}
// Expect filtering because the layer is scaled up.
TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.frameScaleX = 2;
captureArgs.frameScaleY = 2;
@@ -162,7 +148,6 @@
// Expect no filtering because the output buffer's size matches the source crop.
TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -176,7 +161,6 @@
TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) {
Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -187,12 +171,9 @@
// Expect no filtering because the output source crop and output buffer are the same size.
TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- captureArgs.width = 50;
- captureArgs.height = 50;
+ captureArgs.layerHandle = mLayer->getHandle();
captureArgs.sourceCrop = Rect{25, 25, 75, 75};
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
@@ -202,9 +183,8 @@
TEST_F(TextureFilteringTest, LayerCropDisplayFrameMatchNoFiltering) {
Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
@@ -214,9 +194,8 @@
TEST_F(TextureFilteringTest, ParentCropNoFiltering) {
Transaction().setCrop(mParent, Rect{25, 25, 75, 75}).apply();
- gui::DisplayCaptureArgs captureArgs;
- captureArgs.displayToken = mDisplay;
- ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ captureArgs.layerHandle = mLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
@@ -226,7 +205,6 @@
TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) {
Transaction().setPosition(mParent, 100, 100).apply();
- LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mParent->getHandle();
captureArgs.sourceCrop = Rect{0, 0, 100, 100};
ScreenCapture::captureLayers(&mCapture, captureArgs);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 5a3bca1..6e6c6d8 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,7 +45,7 @@
cc_aconfig_library {
name: "libsurfaceflingerflags_test",
aconfig_declarations: "surfaceflinger_flags",
- test: true,
+ mode: "test",
}
cc_test {
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 8e13c0d..ee967979 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -347,7 +347,7 @@
}
static void setupHwcCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+ EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _))
.Times(1);
EXPECT_CALL(*test->mDisplaySurface,
@@ -356,12 +356,12 @@
}
static void setupHwcClientCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+ EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _))
.Times(1);
}
static void setupHwcForcedClientCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _, _)).Times(1);
+ EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
}
static void setupRECompositionCallExpectations(CompositionTest* test) {
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index fa31643..1379665 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -80,7 +80,8 @@
std::unique_ptr<EventThread>(mEventThread),
std::unique_ptr<EventThread>(mSFEventThread),
TestableSurfaceFlinger::DefaultDisplayMode{displayId},
- TestableSurfaceFlinger::SchedulerCallbackImpl::kMock);
+ TestableSurfaceFlinger::SchedulerCallbackImpl::kMock,
+ TestableSurfaceFlinger::VsyncTrackerCallbackImpl::kMock);
}
void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index 0905cd1..aa37754 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -14,130 +14,153 @@
* limitations under the License.
*/
-#include <cstdint>
#undef LOG_TAG
#define LOG_TAG "FlagManagerTest"
#include "FlagManager.h"
+#include "FlagUtils.h"
-#include <android-base/properties.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
-#include <server_configurable_flags/get_flags.h>
-#include <optional>
+
+#include <com_android_graphics_surfaceflinger_flags.h>
namespace android {
using testing::Return;
-class MockFlagManager : public FlagManager {
+class TestableFlagManager : public FlagManager {
public:
- MockFlagManager() = default;
- ~MockFlagManager() = default;
+ TestableFlagManager() : FlagManager(ConstructorTag{}) { markBootCompleted(); }
+ ~TestableFlagManager() = default;
- MOCK_METHOD(std::string, getServerConfigurableFlag, (const std::string& experimentFlagName),
- (const, override));
+ MOCK_METHOD(std::optional<bool>, getBoolProperty, (const char*), (const, override));
+ MOCK_METHOD(bool, getServerConfigurableFlag, (const char*), (const, override));
+
+ void markBootIncomplete() { mBootCompleted = false; }
};
class FlagManagerTest : public testing::Test {
public:
- FlagManagerTest();
- ~FlagManagerTest() override;
- std::unique_ptr<MockFlagManager> mFlagManager;
+ FlagManagerTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+ ~FlagManagerTest() override {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
- template <typename T>
- T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
- T defaultValue);
+ TestableFlagManager mFlagManager;
};
-FlagManagerTest::FlagManagerTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlagManager = std::make_unique<MockFlagManager>();
+TEST_F(FlagManagerTest, isSingleton) {
+ EXPECT_EQ(&FlagManager::getInstance(), &FlagManager::getInstance());
}
-FlagManagerTest::~FlagManagerTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+TEST_F(FlagManagerTest, legacyCreashesIfQueriedBeforeBoot) {
+ mFlagManager.markBootIncomplete();
+ EXPECT_DEATH(FlagManager::getInstance().test_flag(), "");
}
-template <typename T>
-T FlagManagerTest::getValue(const std::string& experimentFlagName,
- std::optional<T> systemPropertyOpt, T defaultValue) {
- return mFlagManager->getValue(experimentFlagName, systemPropertyOpt, defaultValue);
+TEST_F(FlagManagerTest, legacyReturnsOverride) {
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(true));
+ EXPECT_EQ(true, mFlagManager.test_flag());
+
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
+ EXPECT_EQ(false, mFlagManager.test_flag());
}
-namespace {
-TEST_F(FlagManagerTest, getValue_bool_default) {
- EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
- const bool defaultValue = false;
- std::optional<bool> systemPropertyValue = std::nullopt;
- const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, defaultValue);
+TEST_F(FlagManagerTest, legacyReturnsValue) {
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_CALL(mFlagManager, getServerConfigurableFlag).WillOnce(Return(true));
+ EXPECT_EQ(true, mFlagManager.test_flag());
+
+ EXPECT_CALL(mFlagManager, getServerConfigurableFlag).WillOnce(Return(false));
+ EXPECT_EQ(false, mFlagManager.test_flag());
}
-TEST_F(FlagManagerTest, getValue_bool_sysprop) {
- const bool defaultValue = false;
- std::optional<bool> systemPropertyValue = std::make_optional(true);
- const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, true);
+TEST_F(FlagManagerTest, creashesIfQueriedBeforeBoot) {
+ mFlagManager.markBootIncomplete();
+ EXPECT_DEATH(FlagManager::getInstance().late_boot_misc2(), "");
}
-TEST_F(FlagManagerTest, getValue_bool_experiment) {
- EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("1"));
- const bool defaultValue = false;
- std::optional<bool> systemPropertyValue = std::nullopt;
- const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, true);
+TEST_F(FlagManagerTest, returnsOverride) {
+ mFlagManager.setUnitTestMode();
+
+ // Twice, since the first call is to initialize the static variable
+ EXPECT_CALL(mFlagManager, getBoolProperty)
+ .Times((2))
+ .WillOnce(Return(true))
+ .WillOnce(Return(true));
+ EXPECT_EQ(true, mFlagManager.late_boot_misc2());
+
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
+ EXPECT_EQ(false, mFlagManager.late_boot_misc2());
}
-TEST_F(FlagManagerTest, getValue_int32_default) {
- EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
- int32_t defaultValue = 30;
- std::optional<int32_t> systemPropertyValue = std::nullopt;
- int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, defaultValue);
+TEST_F(FlagManagerTest, returnsValue) {
+ mFlagManager.setUnitTestMode();
+
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt));
+
+ {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, true);
+ EXPECT_EQ(true, mFlagManager.late_boot_misc2());
+ }
+
+ {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, false);
+ EXPECT_EQ(false, mFlagManager.late_boot_misc2());
+ }
}
-TEST_F(FlagManagerTest, getValue_int32_sysprop) {
- int32_t defaultValue = 30;
- std::optional<int32_t> systemPropertyValue = std::make_optional(10);
- int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, 10);
+TEST_F(FlagManagerTest, readonlyReturnsOverride) {
+ mFlagManager.setUnitTestMode();
+
+ // Twice, since the first call is to initialize the static variable
+ EXPECT_CALL(mFlagManager, getBoolProperty)
+ .Times(2)
+ .WillOnce(Return(true))
+ .WillOnce(Return(true));
+ EXPECT_EQ(true, mFlagManager.misc1());
+
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
+ EXPECT_EQ(false, mFlagManager.misc1());
}
-TEST_F(FlagManagerTest, getValue_int32_experiment) {
- EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50"));
- std::int32_t defaultValue = 30;
- std::optional<std::int32_t> systemPropertyValue = std::nullopt;
- std::int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, 50);
+TEST_F(FlagManagerTest, readonlyReturnsValue) {
+ mFlagManager.setUnitTestMode();
+
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt));
+
+ {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::misc1, true);
+ EXPECT_EQ(true, mFlagManager.misc1());
+ }
+
+ {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::misc1, false);
+ EXPECT_EQ(false, mFlagManager.misc1());
+ }
}
-TEST_F(FlagManagerTest, getValue_int64_default) {
- EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
- int64_t defaultValue = 30;
- std::optional<int64_t> systemPropertyValue = std::nullopt;
- int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, defaultValue);
+TEST_F(FlagManagerTest, dontSkipOnEarlyIsNotCached) {
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt));
+
+ const auto initialValue = com::android::graphics::surfaceflinger::flags::dont_skip_on_early();
+
+ com::android::graphics::surfaceflinger::flags::dont_skip_on_early(true);
+ EXPECT_EQ(true, mFlagManager.dont_skip_on_early());
+
+ com::android::graphics::surfaceflinger::flags::dont_skip_on_early(false);
+ EXPECT_EQ(false, mFlagManager.dont_skip_on_early());
+
+ com::android::graphics::surfaceflinger::flags::dont_skip_on_early(initialValue);
}
-TEST_F(FlagManagerTest, getValue_int64_sysprop) {
- int64_t defaultValue = 30;
- std::optional<int64_t> systemPropertyValue = std::make_optional(10);
- int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, 10);
-}
-
-TEST_F(FlagManagerTest, getValue_int64_experiment) {
- EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50"));
- int64_t defaultValue = 30;
- std::optional<int64_t> systemPropertyValue = std::nullopt;
- int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
- ASSERT_EQ(result, 50);
-}
-} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/tests/unittests/FlagUtils.h
index 7103684..333e4e7 100644
--- a/services/surfaceflinger/tests/unittests/FlagUtils.h
+++ b/services/surfaceflinger/tests/unittests/FlagUtils.h
@@ -16,12 +16,16 @@
#pragma once
+#include "FlagManager.h"
+
#define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value))
namespace android {
class TestFlagSetter {
public:
TestFlagSetter(bool (*getter)(), void((*setter)(bool)), bool flagValue) {
+ FlagManager::getMutableInstance().setUnitTestMode();
+
const bool initialValue = getter();
setter(flagValue);
mResetFlagValue = [=] { setter(initialValue); };
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 636d852..e040028 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "gmock/gmock-spec-builders.h"
#include "mock/MockTimeStats.h"
#undef LOG_TAG
@@ -1059,6 +1058,94 @@
EXPECT_EQ(received.cookie(), source.cookie());
}
+TEST_F(FrameTimelineTest, traceDisplayFrameSkipped) {
+ // setup 2 display frames
+ // DF 1: [22,40] -> [5, 40]
+ // DF : [36, 70] (Skipped one, added by the trace)
+ // DF 2: [82, 100] -> SF [25, 70]
+ auto tracingSession = getTracingSessionForTest();
+ tracingSession->StartBlocking();
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({82, 90, 100});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ int64_t traceCookie = snoopCurrentTraceCookie();
+
+ // set up 1st display frame
+ FrameTimelineInfo ftInfo1;
+ ftInfo1.vsyncId = surfaceFrameToken1;
+ ftInfo1.inputEventId = sInputEventId;
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo1, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+ surfaceFrame1->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_30);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(30, presentFence1);
+ presentFence1->signalForTest(40);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ FrameTimelineInfo ftInfo2;
+ ftInfo2.vsyncId = surfaceFrameToken2;
+ ftInfo2.inputEventId = sInputEventId;
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+
+ // set up 2nd display frame
+ surfaceFrame2->setAcquireFenceTime(36);
+ mFrameTimeline->setSfWakeUp(sfToken2, 82, RR_11, RR_30);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(90, presentFence2);
+ presentFence2->signalForTest(100);
+
+ // the token of skipped Display Frame
+ auto protoSkippedActualDisplayFrameStart =
+ createProtoActualDisplayFrameStart(traceCookie + 9, 0, kSurfaceFlingerPid,
+ FrameTimelineEvent::PRESENT_DROPPED, true, false,
+ FrameTimelineEvent::JANK_DROPPED,
+ FrameTimelineEvent::PREDICTION_VALID);
+ auto protoSkippedActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 9);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // 8 Valid Display Frames + 8 Valid Surface Frames + 2 Skipped Display Frames
+ EXPECT_EQ(packets.size(), 18u);
+
+ // Packet - 16: Actual skipped Display Frame Start
+ // the timestamp should be equal to the 2nd expected surface frame's end time
+ const auto& packet16 = packets[16];
+ ASSERT_TRUE(packet16.has_timestamp());
+ EXPECT_EQ(packet16.timestamp(), 36u);
+ ASSERT_TRUE(packet16.has_frame_timeline_event());
+
+ const auto& event16 = packet16.frame_timeline_event();
+ const auto& actualSkippedDisplayFrameStart = event16.actual_display_frame_start();
+ validateTraceEvent(actualSkippedDisplayFrameStart, protoSkippedActualDisplayFrameStart);
+
+ // Packet - 17: Actual skipped Display Frame End
+ // the timestamp should be equal to the 2nd expected surface frame's present time
+ const auto& packet17 = packets[17];
+ ASSERT_TRUE(packet17.has_timestamp());
+ EXPECT_EQ(packet17.timestamp(), 70u);
+ ASSERT_TRUE(packet17.has_frame_timeline_event());
+
+ const auto& event17 = packet17.frame_timeline_event();
+ const auto& actualSkippedDisplayFrameEnd = event17.frame_end();
+ validateTraceEvent(actualSkippedDisplayFrameEnd, protoSkippedActualDisplayFrameEnd);
+}
+
TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 4f545a9..788fa51 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -38,9 +38,13 @@
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
#include "DisplayIdentificationTestHelpers.h"
+#include "FlagManager.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 hal::IComposerClient;
using ::testing::_;
using ::testing::DoAll;
-using ::testing::ElementsAreArray;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
@@ -79,10 +82,13 @@
EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
}
- void setDisplayData(HalDisplayId displayId, nsecs_t lastExpectedPresentTimestamp) {
+ void setDisplayData(HalDisplayId displayId, TimePoint lastExpectedPresentTimestamp,
+ Fps lastFrameInterval) {
ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end());
auto& displayData = mHwc.mDisplayData.at(displayId);
+ std::scoped_lock lock{displayData.expectedPresentLock};
displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp;
+ displayData.lastFrameInterval = lastFrameInterval;
}
};
@@ -214,7 +220,102 @@
}
}
-TEST_F(HWComposerTest, getModesWithDisplayConfigurations) {
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) {
+ // if vrr_config is off, getDisplayConfigurationsSupported() is off as well
+ // then getModesWithLegacyDisplayConfigs should be called instead
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, false);
+ ASSERT_FALSE(FlagManager::getInstance().vrr_config());
+
+ constexpr hal::HWDisplayId kHwcDisplayId = 2;
+ constexpr hal::HWConfigId kConfigId = 42;
+ constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
+
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+
+ {
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+ .WillOnce(Return(HalError::BAD_DISPLAY));
+ EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
+ }
+ {
+ constexpr int32_t kWidth = 480;
+ constexpr int32_t kHeight = 720;
+ constexpr int32_t kConfigGroup = 1;
+ constexpr int32_t kVsyncPeriod = 16666667;
+
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
+ _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kWidth), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kHeight), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kConfigGroup), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId,
+ IComposerClient::Attribute::VSYNC_PERIOD, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(kVsyncPeriod), Return(HalError::NONE)));
+
+ // Optional Parameters UNSUPPORTED
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+ _))
+ .WillOnce(Return(HalError::UNSUPPORTED));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+ _))
+ .WillOnce(Return(HalError::UNSUPPORTED));
+
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{kConfigId}),
+ Return(HalError::NONE)));
+
+ auto modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+
+ // Optional parameters are supported
+ constexpr int32_t kDpi = 320;
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+ _))
+ .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+ EXPECT_CALL(*mHal,
+ getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+ _))
+ .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+
+ modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.size(), size_t{1});
+ EXPECT_EQ(modes.front().hwcId, kConfigId);
+ EXPECT_EQ(modes.front().width, kWidth);
+ EXPECT_EQ(modes.front().height, kHeight);
+ EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+ EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ // DPI values are scaled by 1000 in the legacy implementation.
+ EXPECT_EQ(modes.front().dpiX, kDpi / 1000.f);
+ EXPECT_EQ(modes.front().dpiY, kDpi / 1000.f);
+ }
+}
+
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true);
+ ASSERT_TRUE(FlagManager::getInstance().vrr_config());
+
constexpr hal::HWDisplayId kHwcDisplayId = 2;
constexpr hal::HWConfigId kConfigId = 42;
constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
@@ -322,48 +423,137 @@
ASSERT_TRUE(info);
auto expectedPresentTime = systemTime() + ms2ns(10);
- const int32_t frameIntervalNs = static_cast<Fps>(60_Hz).getPeriodNsecs();
- static constexpr nsecs_t kTimeoutNs = ms2ns(30);
+ static constexpr Fps Fps60Hz = 60_Hz;
+ static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameInterval60HzNs = Fps60Hz.getPeriodNsecs();
+ static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs();
+ static constexpr Period kVsyncPeriod =
+ Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs());
+ static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs);
+ static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0);
- ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, /* lastExpectedPresentTimestamp= */ 0));
+ ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, kLastExpectedPresentTimestamp, Fps60Hz));
{
// Very first ExpectedPresent after idle, no previous timestamp
EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
.WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+ mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), Fps60Hz,
kTimeoutNs);
}
{
- // ExpectedPresent is after the timeoutNs
- expectedPresentTime += ms2ns(50);
+ // Absent timeoutNs
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+ mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), Fps60Hz,
+ /*timeoutOpt*/ std::nullopt);
+ }
+ {
+ // Timeout is 0
+ expectedPresentTime += kFrameInterval60HzNs;
EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
.WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+ mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), Fps60Hz,
+ Period::fromNs(0));
+ }
+ {
+ // ExpectedPresent is after the timeoutNs
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ EXPECT_CALL(*mHal,
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
+ .WillOnce(Return(HalError::NONE));
+ mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), Fps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent has not changed
+ EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+ mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), Fps60Hz,
kTimeoutNs);
}
{
// ExpectedPresent is after the last reported ExpectedPresent.
- expectedPresentTime += ms2ns(10);
+ expectedPresentTime += kFrameInterval60HzNs;
EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
- mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+ mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), Fps60Hz,
kTimeoutNs);
}
{
// ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs,
// representing we changed our decision and want to present earlier than previously
// reported.
- expectedPresentTime -= ms2ns(20);
+ expectedPresentTime -= kFrameInterval120HzNs;
EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
.WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+ mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), Fps60Hz,
kTimeoutNs);
}
}
+TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) {
+ constexpr hal::HWDisplayId kHwcDisplayId = 2;
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ const auto now = systemTime();
+ auto expectedPresentTime = now;
+ static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
+
+ ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, TimePoint::fromNs(now), Fps::fromValue(0)));
+ static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs();
+ static constexpr Period kVsyncPeriod =
+ Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs());
+
+ struct FrameRateIntervalTestData {
+ int32_t frameIntervalNs;
+ bool callExpectedPresent;
+ };
+ const std::vector<FrameRateIntervalTestData> frameIntervals = {
+ {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true},
+ {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true},
+ {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true},
+ {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false},
+ {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true},
+ {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true},
+ {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true},
+ };
+
+ for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) {
+ {
+ expectedPresentTime += frameIntervalNs;
+ if (callExpectedPresent) {
+ EXPECT_CALL(*mHal,
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime,
+ frameIntervalNs))
+ .WillOnce(Return(HalError::NONE));
+ } else {
+ EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+ }
+ mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime),
+ Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs);
+ }
+ }
+}
+
struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection));
MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId));
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 631adf1..befef48 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -18,12 +18,14 @@
#define LOG_TAG "LayerHistoryIntegrationTest"
#include <Layer.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <renderengine/mock/FakeExternalTexture.h>
+#include "FlagUtils.h"
#include "FpsOps.h"
#include "LayerHierarchyTest.h"
#include "Scheduler/LayerHistory.h"
@@ -36,6 +38,7 @@
namespace android::scheduler {
using android::mock::createDisplayMode;
+using namespace com::android::graphics::surfaceflinger;
class LayerHistoryIntegrationTest : public surfaceflinger::frontend::LayerSnapshotTestBase {
protected:
@@ -162,8 +165,10 @@
DisplayModeId(0));
mock::SchedulerCallback mSchedulerCallback;
+ mock::VsyncTrackerCallback mVsyncTrackerCallback;
- TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
TestableSurfaceFlinger mFlinger;
};
@@ -492,7 +497,9 @@
EXPECT_EQ(1, frequentLayerCount(time));
}
-TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayer) {
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayerIsActive) {
+ SET_FLAG_FOR_TEST(flags::misc1, false);
+
auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
hideLayer(2);
@@ -515,6 +522,31 @@
EXPECT_EQ(2, frequentLayerCount(time));
}
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayerIsNotActive) {
+ SET_FLAG_FOR_TEST(flags::misc1, true);
+
+ auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+ auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+ hideLayer(2);
+ setFrameRate(1, 60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRate(2, 90.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ setBufferWithPresentTime(explicitVisiblelayer, time);
+ setBufferWithPresentTime(explicitInvisiblelayer, time);
+
+ EXPECT_EQ(2u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryIntegrationTest, infrequentAnimatingLayer) {
auto layer = createLegacyAndFrontedEndLayer(1);
@@ -807,6 +839,81 @@
ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
}
+TEST_F(LayerHistoryIntegrationTest, hidingLayerUpdatesLayerHistory) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+
+ hideLayer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summary.empty());
+ EXPECT_EQ(0u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, showingLayerUpdatesLayerHistory) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ hideLayer(1);
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ auto summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summary.empty());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ showLayer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, updatingGeometryUpdatesWeight) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1,
+ std::make_shared<
+ renderengine::mock::FakeExternalTexture>(100U /*width*/, 100U /*height*/, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_PROTECTED /*usage*/));
+ mFlinger.setLayerHistoryDisplayArea(100 * 100);
+ updateLayerSnapshotsAndLayerHistory(time);
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+
+ auto startingWeight = summary[0].weight;
+
+ setMatrix(1, 0.1f, 0.f, 0.f, 0.1f);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_GT(startingWeight, summary[0].weight);
+}
+
class LayerHistoryIntegrationTestParameterized
: public LayerHistoryIntegrationTest,
public testing::WithParamInterface<std::chrono::nanoseconds> {};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 33c1d86..1adf14f 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -22,10 +22,12 @@
#define LOG_TAG "LayerHistoryTest"
#include <Layer.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
+#include "FlagUtils.h"
#include "FpsOps.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/LayerInfo.h"
@@ -142,13 +144,17 @@
mock::SchedulerCallback mSchedulerCallback;
- TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+ mock::VsyncTrackerCallback mVsyncTrackerCallback;
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
TestableSurfaceFlinger mFlinger;
};
namespace {
+using namespace com::android::graphics::surfaceflinger;
+
TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
@@ -555,6 +561,33 @@
EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
}
+TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) {
+ SET_FLAG_FOR_TEST(flags::misc1, true);
+
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(12.34_Hz, Layer::FrameRateCompatibility::Default,
+ Seamlessness::OnlySeamless, FrameRateCategory::High)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have
+ // votes to consider for refresh rate selection.
+ ASSERT_EQ(0, summarizeLayerHistory(time).size());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryTest, multipleLayers) {
auto layer1 = createLayer("A");
auto layer2 = createLayer("B");
@@ -780,6 +813,8 @@
}
TEST_F(LayerHistoryTest, invisibleExplicitLayer) {
+ SET_FLAG_FOR_TEST(flags::misc1, false);
+
auto explicitVisiblelayer = createLayer();
auto explicitInvisiblelayer = createLayer();
@@ -810,6 +845,39 @@
EXPECT_EQ(2, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTest, invisibleExplicitLayerDoesNotVote) {
+ SET_FLAG_FOR_TEST(flags::misc1, true);
+
+ auto explicitVisiblelayer = createLayer();
+ auto explicitInvisiblelayer = createLayer();
+
+ EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
+ time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
+ time, time, LayerHistory::LayerUpdateType::Buffer);
+
+ EXPECT_EQ(2, layerCount());
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryTest, infrequentAnimatingLayer) {
auto layer = createLayer();
@@ -859,6 +927,43 @@
EXPECT_EQ(1, animatingLayerCount(time));
}
+TEST_F(LayerHistoryTest, frontBufferedLayerVotesMax) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, isFrontBuffered()).WillRepeatedly(Return(true));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+}
+
TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) {
auto layer = createLayer();
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 11072bc..047ef5a 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -21,6 +21,7 @@
#include <scheduler/Fps.h>
+#include "FlagUtils.h"
#include "FpsOps.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/LayerInfo.h"
@@ -28,6 +29,8 @@
#include "TestableSurfaceFlinger.h"
#include "mock/MockSchedulerCallback.h"
+#include <com_android_graphics_surfaceflinger_flags.h>
+
namespace android::scheduler {
using android::mock::createDisplayMode;
@@ -61,12 +64,16 @@
HI_FPS)),
DisplayModeId(0));
mock::SchedulerCallback mSchedulerCallback;
- TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+ mock::VsyncTrackerCallback mVsyncTrackerCallback;
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
TestableSurfaceFlinger mFlinger;
};
namespace {
+using namespace com::android::graphics::surfaceflinger;
+
TEST_F(LayerInfoTest, prefersPresentTime) {
std::deque<FrameTimeData> frameTimes;
constexpr auto kExpectedFps = 50_Hz;
@@ -261,5 +268,18 @@
ASSERT_EQ(actualVotes[0].fps, vote.fps);
}
+TEST_F(LayerInfoTest, isFrontBuffered) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ ASSERT_FALSE(layerInfo.isFrontBuffered());
+
+ LayerProps prop = {.isFrontBuffered = true};
+ layerInfo.setLastPresentTime(0, 0, LayerHistory::LayerUpdateType::Buffer, true, prop);
+ ASSERT_TRUE(layerInfo.isFrontBuffered());
+
+ prop.isFrontBuffered = false;
+ layerInfo.setLastPresentTime(0, 0, LayerHistory::LayerUpdateType::Buffer, true, prop);
+ ASSERT_FALSE(layerInfo.isFrontBuffered());
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index e784eb7..2cc6dc7 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -789,6 +789,143 @@
EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12 (frame rate set to default with strategy default)
+ // │ │ ├── 121
+ // │ │ └── 122 (frame rate set to 123.f)
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+ setFrameRate(12, -1.f, 0, 0);
+ setFrameRateSelectionStrategy(12, 0 /* Default */);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent 1 gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+ EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_FALSE(getSnapshot({.id = 121})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 123.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 123.f);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+}
+
+TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) {
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12 (frame rate category set to high with strategy OverrideChildren)
+ // │ │ ├── 121
+ // │ │ └── 122 (frame rate set to 123.f but should be overridden by layer 12)
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+ setFrameRateCategory(12, 4 /* high */);
+ setFrameRate(122, 123.f, 0, 0);
+ setFrameRateSelectionStrategy(12, 1 /* OverrideChildren */);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent 1 gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.category, FrameRateCategory::High);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.category, FrameRateCategory::High);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::High);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::High);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12 (frame rate category to default with strategy default)
+ // │ │ ├── 121
+ // │ │ └── 122 (frame rate set to 123.f)
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+ setFrameRateCategory(12, 0 /* default */);
+ setFrameRateSelectionStrategy(12, 0 /* Default */);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent 1 gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+ EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 123.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 123.f);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
}
TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) {
@@ -898,4 +1035,24 @@
gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
}
+TEST_F(LayerSnapshotTest, isFrontBuffered) {
+ setBuffer(1,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(
+ 1U /*width*/, 1U /*height*/, 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_HW_TEXTURE | AHARDWAREBUFFER_USAGE_FRONT_BUFFER /*usage*/));
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot(1)->isFrontBuffered());
+
+ setBuffer(1,
+ std::make_shared<
+ renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ 1ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_HW_TEXTURE /*usage*/));
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_FALSE(getSnapshot(1)->isFrontBuffered());
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 87fae2c..b5eb777 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -89,7 +89,9 @@
kDisplay1Mode60->getId());
mock::SchedulerCallback mSchedulerCallback;
- TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
+ mock::VsyncTrackerCallback mVsyncTrackerCallback;
+ TestableScheduler* mScheduler =
+ new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback};
surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
ConnectionHandle mConnectionHandle;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index d0290ea..b80cb66 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -103,7 +103,7 @@
EXPECT_CALL(*mDisplaySurface,
prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
.Times(1);
- EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)).WillOnce([] {
+ EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
constexpr Duration kMockHwcRunTime = 20ms;
std::this_thread::sleep_for(kMockHwcRunTime);
return hardware::graphics::composer::V2_1::Error::NONE;
@@ -124,7 +124,7 @@
EXPECT_CALL(*mDisplaySurface,
prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
.Times(1);
- EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)).WillOnce([] {
+ EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
constexpr Duration kMockHwcRunTime = 20ms;
std::this_thread::sleep_for(kMockHwcRunTime);
return hardware::graphics::composer::V2_1::Error::NONE;
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3d1c900..8b6f0f1 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -36,18 +36,21 @@
class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
+ TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
- sp<VsyncModulator>::make(VsyncConfigSet{}), callback) {}
+ sp<VsyncModulator>::make(VsyncConfigSet{}), callback,
+ vsyncTrackerCallback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
- sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
- : Scheduler(*this, callback,
+ sp<VsyncModulator> modulatorPtr, ISchedulerCallback& schedulerCallback,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
+ : Scheduler(*this, schedulerCallback,
(FeatureFlags)Feature::kContentDetection |
Feature::kSmallDirtyContentDetection,
- std::move(modulatorPtr)) {
+ std::move(modulatorPtr), vsyncTrackerCallback) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
std::move(tracker));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8f1982d..bca14f5 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -53,6 +53,7 @@
#include "mock/MockFrameTimeline.h"
#include "mock/MockFrameTracer.h"
#include "mock/MockSchedulerCallback.h"
+#include "mock/MockVsyncTrackerCallback.h"
#include "mock/system/window/MockNativeWindow.h"
#include "Scheduler/VSyncTracker.h"
@@ -204,6 +205,8 @@
enum class SchedulerCallbackImpl { kNoOp, kMock };
+ enum class VsyncTrackerCallbackImpl { kNoOp, kMock };
+
struct DefaultDisplayMode {
// The ID of the injected RefreshRateSelector and its default display mode.
PhysicalDisplayId displayId;
@@ -213,13 +216,14 @@
using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>;
- void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
- std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
- std::unique_ptr<EventThread> appEventThread,
- std::unique_ptr<EventThread> sfEventThread,
- DisplayModesVariant modesVariant,
- SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
- bool useNiceMock = false) {
+ void setupScheduler(
+ std::unique_ptr<scheduler::VsyncController> vsyncController,
+ std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
+ std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread,
+ DisplayModesVariant modesVariant,
+ SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
+ VsyncTrackerCallbackImpl vsyncTrackerCallbackImpl = VsyncTrackerCallbackImpl::kNoOp,
+ bool useNiceMock = false) {
RefreshRateSelectorPtr selectorPtr = ftl::match(
modesVariant,
[](DefaultDisplayMode arg) {
@@ -239,10 +243,16 @@
mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
- using Callback = scheduler::ISchedulerCallback;
- Callback& callback = callbackImpl == SchedulerCallbackImpl::kNoOp
- ? static_cast<Callback&>(mNoOpSchedulerCallback)
- : static_cast<Callback&>(mSchedulerCallback);
+ using ISchedulerCallback = scheduler::ISchedulerCallback;
+ ISchedulerCallback& schedulerCallback = callbackImpl == SchedulerCallbackImpl::kNoOp
+ ? static_cast<ISchedulerCallback&>(mNoOpSchedulerCallback)
+ : static_cast<ISchedulerCallback&>(mSchedulerCallback);
+
+ using VsyncTrackerCallback = scheduler::IVsyncTrackerCallback;
+ VsyncTrackerCallback& vsyncTrackerCallback =
+ vsyncTrackerCallbackImpl == VsyncTrackerCallbackImpl::kNoOp
+ ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback)
+ : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback);
auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -253,12 +263,14 @@
std::move(vsyncTracker),
std::move(selectorPtr),
std::move(modulatorPtr),
- callback);
+ schedulerCallback,
+ vsyncTrackerCallback);
} else {
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker),
std::move(selectorPtr),
- std::move(modulatorPtr), callback);
+ std::move(modulatorPtr),
+ schedulerCallback, vsyncTrackerCallback);
}
mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms);
@@ -271,7 +283,7 @@
resetScheduler(mScheduler);
}
- void setupMockScheduler(test::MockSchedulerOptions options = {}) {
+ void setupMockScheduler(surfaceflinger::test::MockSchedulerOptions options = {}) {
using testing::_;
using testing::Return;
@@ -297,7 +309,8 @@
EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread),
std::move(sfEventThread), DefaultDisplayMode{options.displayId},
- SchedulerCallbackImpl::kNoOp, options.useNiceMock);
+ SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp,
+ options.useNiceMock);
}
void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
@@ -610,6 +623,9 @@
void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
+ auto setLayerHistoryDisplayArea(uint32_t displayArea) {
+ return mFlinger->mScheduler->onActiveDisplayAreaChanged(displayArea);
+ };
auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
auto setDaltonizerType(ColorBlindnessType type) {
mFlinger->mDaltonizer.setType(type);
@@ -1071,6 +1087,8 @@
sp<SurfaceFlinger> mFlinger;
scheduler::mock::SchedulerCallback mSchedulerCallback;
scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
+ scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback;
+ scheduler::mock::NoOpVsyncTrackerCallback mNoOpVsyncTrackerCallback;
std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
scheduler::TestableScheduler* mScheduler = nullptr;
Hwc2::mock::PowerAdvisor mPowerAdvisor;
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 00b5bf0..d4d5b32 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -112,7 +112,7 @@
EXPECT_CALL(*mFlinger.getFrameTracer(),
traceFence(layerId, bufferId, frameNumber, presentFence,
FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0));
- layer->onPostComposition(nullptr, glDoneFence, presentFence, compositorTiming);
+ layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming);
}
};
diff --git a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp
index 379135e..4a83d44 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp
@@ -45,12 +45,21 @@
TestableSurfaceFlinger mFlinger;
};
-TEST_F(TransactionTraceWriterTest, canWriteToFile) {
+// Check that a new file is written if overwrite=true and no file exists.
+TEST_F(TransactionTraceWriterTest, canWriteToFile_overwriteTrue) {
TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true);
EXPECT_EQ(access(mFilename.c_str(), F_OK), 0);
verifyTraceFile();
}
+// Check that a new file is written if overwrite=false and no file exists.
+TEST_F(TransactionTraceWriterTest, canWriteToFile_overwriteFalse) {
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ false);
+ EXPECT_EQ(access(mFilename.c_str(), F_OK), 0);
+ verifyTraceFile();
+}
+
+// Check that an existing file is overwritten when overwrite=true.
TEST_F(TransactionTraceWriterTest, canOverwriteFile) {
std::string testLine = "test";
{
@@ -61,6 +70,7 @@
verifyTraceFile();
}
+// Check that an existing file isn't overwritten when it is new and overwrite=false.
TEST_F(TransactionTraceWriterTest, doNotOverwriteFile) {
std::string testLine = "test";
{
@@ -76,4 +86,23 @@
EXPECT_EQ(line, testLine);
}
}
+
+// Check that an existing file is overwritten when it is old and overwrite=false.
+TEST_F(TransactionTraceWriterTest, overwriteOldFile) {
+ std::string testLine = "test";
+ {
+ std::ofstream file(mFilename, std::ios::out);
+ file << testLine;
+ }
+
+ // Update file modification time to 15 minutes ago.
+ using Clock = std::filesystem::file_time_type::clock;
+ std::error_code error;
+ std::filesystem::last_write_time(mFilename, Clock::now() - std::chrono::minutes{15}, error);
+ ASSERT_EQ(error.value(), 0);
+
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ false);
+ verifyTraceFile();
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 41866a1..4be07a1 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -54,7 +54,7 @@
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
- void setRenderRate(Fps) final {}
+ void setDisplayModeData(const DisplayModeData&) final {}
void dump(std::string&) const final {}
private:
@@ -92,7 +92,7 @@
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
- void setRenderRate(Fps) final {}
+ void setDisplayModeData(const DisplayModeData&) final {}
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 1dc5498..8310866 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -59,7 +59,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
- MOCK_METHOD(void, setRenderRate, (Fps), (override));
+ MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
@@ -783,7 +783,9 @@
TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTargetVSync) {
SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
- EXPECT_CALL(mMockClock, alarmAt(_, 500));
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 400)).InSequence(seq);
CountingCallback cb(mDispatch);
auto result =
mDispatch->schedule(cb,
@@ -873,7 +875,9 @@
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) {
SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
- EXPECT_CALL(mMockClock, alarmAt(_, 600));
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq);
CountingCallback cb(mDispatch);
auto result =
@@ -1119,6 +1123,7 @@
Sequence seq;
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq);
CountingCallback cb(mDispatch);
@@ -1132,7 +1137,7 @@
ASSERT_THAT(cb.mCalls.size(), Eq(1));
EXPECT_THAT(cb.mCalls[0], Eq(1000));
ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
- EXPECT_THAT(cb.mWakeupTime[0], Eq(600));
+ EXPECT_THAT(cb.mWakeupTime[0], Eq(0));
ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
EXPECT_THAT(cb.mReadyTime[0], Eq(1000));
}
@@ -1161,7 +1166,9 @@
TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) {
SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
- EXPECT_CALL(mMockClock, alarmAt(_, 500));
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 300)).InSequence(seq);
CountingCallback cb(mDispatch);
auto result =
mDispatch->schedule(cb,
@@ -1177,6 +1184,11 @@
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb.mCalls[0], Eq(1000));
+ ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb.mWakeupTime[0], Eq(300));
+ ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb.mReadyTime[0], Eq(1000));
}
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 43d683d..30a2855 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -23,7 +23,9 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#define LOG_NDEBUG 0
+#include "FlagUtils.h"
#include "Scheduler/VSyncPredictor.h"
+#include "mock/MockVsyncTrackerCallback.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -31,8 +33,11 @@
#include <chrono>
#include <utility>
+#include <com_android_graphics_surfaceflinger_flags.h>
+
using namespace testing;
using namespace std::literals;
+using namespace com::android::graphics::surfaceflinger;
namespace android::scheduler {
@@ -52,13 +57,18 @@
struct VSyncPredictorTest : testing::Test {
nsecs_t mNow = 0;
nsecs_t mPeriod = 1000;
+ scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback;
static constexpr size_t kHistorySize = 10;
static constexpr size_t kMinimumSamplesForPrediction = 6;
static constexpr size_t kOutlierTolerancePercent = 25;
static constexpr nsecs_t mMaxRoundingError = 100;
- VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, kHistorySize, kMinimumSamplesForPrediction,
- kOutlierTolerancePercent};
+ VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
+ mPeriod,
+ kHistorySize,
+ kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent,
+ mVsyncTrackerCallback};
};
TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
@@ -378,8 +388,12 @@
// See b/151146131
TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
- VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, 20, kMinimumSamplesForPrediction,
- kOutlierTolerancePercent};
+ VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
+ mPeriod,
+ 20,
+ kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent,
+ mVsyncTrackerCallback};
std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
840923581635, 840940161584, 840956868096,
840973702473, 840990256277, 841007116851,
@@ -566,7 +580,7 @@
tracker.addVsyncTimestamp(mNow);
}
- tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod));
+ tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3 * mPeriod)});
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
@@ -588,12 +602,12 @@
const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
- tracker.setRenderRate(refreshRate / 4);
+ tracker.setDisplayModeData({.renderRate = refreshRate / 4});
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod));
- tracker.setRenderRate(refreshRate / 2);
+ tracker.setDisplayModeData({.renderRate = refreshRate / 2});
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod));
@@ -601,7 +615,7 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod));
- tracker.setRenderRate(refreshRate / 6);
+ tracker.setDisplayModeData({.renderRate = refreshRate / 6});
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod));
}
@@ -615,7 +629,7 @@
tracker.addVsyncTimestamp(mNow);
}
- tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod));
+ tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3.5f * mPeriod)});
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
@@ -626,6 +640,39 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}
+TEST_F(VSyncPredictorTest, vsyncTrackerCallback) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
+ DisplayModeData displayModeData =
+ DisplayModeData{.renderRate = refreshRate,
+ .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)};
+ tracker.setDisplayModeData(displayModeData);
+ auto last = mNow;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_CALL(mVsyncTrackerCallback,
+ onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(last + mPeriod),
+ displayModeData, Period::fromNs(mPeriod)))
+ .Times(1);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ }
+
+ displayModeData = DisplayModeData{.renderRate = refreshRate / 2,
+ .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)};
+ tracker.setDisplayModeData(displayModeData);
+ {
+ // out of render rate phase
+ EXPECT_CALL(mVsyncTrackerCallback,
+ onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(mNow + 3 * mPeriod),
+ displayModeData, Period::fromNs(mPeriod)))
+ .Times(1);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod),
+ Eq(mNow + 3 * mPeriod));
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 122192b..aca3ccc 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -50,7 +50,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
- MOCK_METHOD(void, setRenderRate, (Fps), (override));
+ MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 0b07745..3b74f0a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -95,9 +95,9 @@
MOCK_METHOD2(setPowerMode, Error(Display, IComposerClient::PowerMode));
MOCK_METHOD2(setVsyncEnabled, Error(Display, IComposerClient::Vsync));
MOCK_METHOD1(setClientTargetSlotCount, Error(Display));
- MOCK_METHOD4(validateDisplay, Error(Display, nsecs_t, uint32_t*, uint32_t*));
- MOCK_METHOD6(presentOrValidateDisplay,
- Error(Display, nsecs_t, uint32_t*, uint32_t*, int*, uint32_t*));
+ MOCK_METHOD(Error, validateDisplay, (Display, nsecs_t, int32_t, uint32_t*, uint32_t*));
+ MOCK_METHOD(Error, presentOrValidateDisplay,
+ (Display, nsecs_t, int32_t, uint32_t*, uint32_t*, int*, uint32_t*));
MOCK_METHOD4(setCursorPosition, Error(Display, Layer, int32_t, int32_t));
MOCK_METHOD5(setLayerBuffer, Error(Display, Layer, uint32_t, const sp<GraphicBuffer>&, int));
MOCK_METHOD4(setLayerBufferSlotsToClear,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 40f59b8..a7ddb6d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -76,9 +76,9 @@
(override));
MOCK_METHOD(hal::Error, setPowerMode, (hal::PowerMode), (override));
MOCK_METHOD(hal::Error, setVsyncEnabled, (hal::Vsync), (override));
- MOCK_METHOD(hal::Error, validate, (nsecs_t, uint32_t *, uint32_t *), (override));
+ MOCK_METHOD(hal::Error, validate, (nsecs_t, int32_t, uint32_t*, uint32_t*), (override));
MOCK_METHOD(hal::Error, presentOrValidate,
- (nsecs_t, uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *),
+ (nsecs_t, int32_t, uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*),
(override));
MOCK_METHOD(ftl::Future<hal::Error>, setDisplayBrightness,
(float, float, const Hwc2::Composer::DisplayBrightnessOptions &), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 4cc78fe..3dfb649 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -37,6 +37,7 @@
MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility());
MOCK_CONST_METHOD0(getOwnerUid, uid_t());
MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
+ MOCK_METHOD(bool, isFrontBuffered, (), (const, override));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index dcf25e1..31eb86e 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -34,7 +34,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
- MOCK_METHOD(void, setRenderRate, (Fps), (override));
+ MOCK_METHOD(void, setDisplayModeData, (const scheduler::DisplayModeData&), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h
new file mode 100644
index 0000000..b8e24e0
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VSyncTracker.h"
+
+namespace android::scheduler::mock {
+
+struct VsyncTrackerCallback final : IVsyncTrackerCallback {
+ MOCK_METHOD(void, onVsyncGenerated,
+ (PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, Period),
+ (override));
+};
+
+struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback {
+ void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&,
+ Period) override{};
+};
+} // namespace android::scheduler::mock
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 5d7a4aa..cae51a5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -183,8 +183,12 @@
.library_namespace = library_namespace,
};
so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
- ALOGE("Could not load %s from updatable gfx driver namespace: %s.",
- lib_name.c_str(), dlerror());
+ if (!so) {
+ ALOGE(
+ "Could not load %s from updatable gfx driver namespace: "
+ "%s.",
+ lib_name.c_str(), dlerror());
+ }
} else {
// load built-in driver
so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);